56 #include <QApplication>
58 #include <QHBoxLayout>
61 #include <QDoubleSpinBox>
62 #include <QMessageBox>
63 #include <QPushButton>
64 #include <QStackedWidget>
65 #include <QTableWidget>
66 #include <QTreeWidget>
67 #include <QTreeWidgetItem>
68 #include <QVBoxLayout>
71 #include <boost/graph/adjacency_list.hpp>
72 #include <boost/graph/depth_first_search.hpp>
77 static const std::string
VIS_TOPIC_NAME =
"planning_components_visualization";
80 struct CycleDetector :
public boost::dfs_visitor<>
86 template <
class Edge,
class Graph>
103 QVBoxLayout* layout =
new QVBoxLayout();
106 HeaderWidget*
header =
new HeaderWidget(
107 "Define Planning Groups",
108 "Create and edit 'joint model' groups for your robot based on joint collections, "
109 "link collections, kinematic chains or subgroups. "
110 "A planning group defines the set of (joint, link) pairs considered for planning "
111 "and collision checking. Define individual groups for each subset of the robot you want to plan for.\n"
112 "Note: when adding a link to the group, its parent joint is added too and vice versa.",
114 layout->addWidget(header);
119 groups_tree_widget_ = createContentsWidget();
123 connect(joints_widget_, SIGNAL(cancelEditing()),
this, SLOT(cancelEditing()));
124 connect(joints_widget_, SIGNAL(doneEditing()),
this, SLOT(saveJointsScreen()));
125 connect(joints_widget_, SIGNAL(previewSelected(std::vector<std::string>)),
this,
126 SLOT(previewSelectedJoints(std::vector<std::string>)));
130 connect(links_widget_, SIGNAL(cancelEditing()),
this, SLOT(cancelEditing()));
131 connect(links_widget_, SIGNAL(doneEditing()),
this, SLOT(saveLinksScreen()));
132 connect(links_widget_, SIGNAL(previewSelected(std::vector<std::string>)),
this,
133 SLOT(previewSelectedLink(std::vector<std::string>)));
137 connect(chain_widget_, SIGNAL(cancelEditing()),
this, SLOT(cancelEditing()));
138 connect(chain_widget_, SIGNAL(doneEditing()),
this, SLOT(saveChainScreen()));
140 connect(chain_widget_, SIGNAL(
highlightLink(
const std::string&,
const QColor&)),
this,
145 connect(subgroups_widget_, SIGNAL(cancelEditing()),
this, SLOT(cancelEditing()));
146 connect(subgroups_widget_, SIGNAL(doneEditing()),
this, SLOT(saveSubgroupsScreen()));
147 connect(subgroups_widget_, SIGNAL(previewSelected(std::vector<std::string>)),
this,
148 SLOT(previewSelectedSubgroup(std::vector<std::string>)));
152 connect(group_edit_widget_, SIGNAL(cancelEditing()),
this, SLOT(cancelEditing()));
153 connect(group_edit_widget_, SIGNAL(deleteGroup()),
this, SLOT(deleteGroup()));
154 connect(group_edit_widget_, SIGNAL(save()),
this, SLOT(saveGroupScreenEdit()));
155 connect(group_edit_widget_, SIGNAL(saveJoints()),
this, SLOT(saveGroupScreenJoints()));
156 connect(group_edit_widget_, SIGNAL(saveLinks()),
this, SLOT(saveGroupScreenLinks()));
157 connect(group_edit_widget_, SIGNAL(saveChain()),
this, SLOT(saveGroupScreenChain()));
158 connect(group_edit_widget_, SIGNAL(saveSubgroups()),
this, SLOT(saveGroupScreenSubgroups()));
161 stacked_widget_ =
new QStackedWidget(
this);
162 stacked_widget_->addWidget(groups_tree_widget_);
163 stacked_widget_->addWidget(joints_widget_);
164 stacked_widget_->addWidget(links_widget_);
165 stacked_widget_->addWidget(chain_widget_);
166 stacked_widget_->addWidget(subgroups_widget_);
167 stacked_widget_->addWidget(group_edit_widget_);
173 layout->addWidget(stacked_widget_);
177 QApplication::processEvents();
186 QWidget* content_widget =
new QWidget(
this);
189 QVBoxLayout* layout =
new QVBoxLayout(
this);
201 QHBoxLayout* controls_layout =
new QHBoxLayout();
204 QLabel* expand_controls =
new QLabel(
this);
205 expand_controls->setText(
"<a href='expand'>Expand All</a> <a href='contract'>Collapse All</a>");
206 connect(expand_controls, SIGNAL(linkActivated(
const QString)),
this, SLOT(
alterTree(
const QString)));
207 controls_layout->addWidget(expand_controls);
210 controls_layout->addItem(
new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
213 btn_delete_ =
new QPushButton(
"&Delete Selected",
this);
214 btn_delete_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
218 controls_layout->setAlignment(
btn_delete_, Qt::AlignRight);
221 btn_edit_ =
new QPushButton(
"&Edit Selected",
this);
222 btn_edit_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
227 controls_layout->setAlignment(
btn_edit_, Qt::AlignRight);
230 QPushButton* btn_add =
new QPushButton(
"&Add Group",
this);
231 btn_add->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
232 btn_add->setMaximumWidth(300);
233 connect(btn_add, SIGNAL(clicked()),
this, SLOT(
addGroup()));
234 controls_layout->addWidget(btn_add);
235 controls_layout->setAlignment(btn_add, Qt::AlignRight);
238 layout->addLayout(controls_layout);
241 content_widget->setLayout(layout);
243 return content_widget;
287 const QFont top_level_font(QFont().defaultFamily(), 11, QFont::Bold);
288 const QFont type_font(QFont().defaultFamily(), 11, QFont::Normal, QFont::StyleItalic);
290 QTreeWidgetItem*
group;
293 if (parent ==
nullptr)
297 group->setFont(0, top_level_font);
303 group =
new QTreeWidgetItem(parent);
305 group->setFont(0, top_level_font);
307 parent->addChild(
group);
311 QTreeWidgetItem* joints =
new QTreeWidgetItem(
group);
312 joints->setText(0,
"Joints");
313 joints->setFont(0, type_font);
314 joints->setData(0, Qt::UserRole, QVariant::fromValue(
PlanGroupType(&group_it,
JOINT)));
315 group->addChild(joints);
318 const moveit::core::RobotModelConstPtr& model =
config_data_->getRobotModel();
321 for (std::vector<std::string>::const_iterator joint_it = group_it.
joints_.begin(); joint_it != group_it.
joints_.end();
324 QTreeWidgetItem* j =
new QTreeWidgetItem(joints);
326 std::string joint_name;
332 joint_name = *joint_it +
" - " + jm->
getTypeName();
336 joint_name = *joint_it;
340 j->setText(0, joint_name.c_str());
345 QTreeWidgetItem* links =
new QTreeWidgetItem(group);
346 links->setText(0,
"Links");
347 links->setFont(0, type_font);
348 links->setData(0, Qt::UserRole, QVariant::fromValue(
PlanGroupType(&group_it,
LINK)));
349 group->addChild(links);
352 for (std::vector<std::string>::const_iterator joint_it = group_it.
links_.begin(); joint_it != group_it.
links_.end();
355 QTreeWidgetItem* j =
new QTreeWidgetItem(links);
356 j->setData(0, Qt::UserRole, QVariant::fromValue(
PlanGroupType(&group_it,
LINK)));
357 j->setText(0, joint_it->c_str());
362 QTreeWidgetItem* chains =
new QTreeWidgetItem(group);
363 chains->setText(0,
"Chain");
364 chains->setFont(0, type_font);
365 chains->setData(0, Qt::UserRole, QVariant::fromValue(
PlanGroupType(&group_it,
CHAIN)));
366 group->addChild(chains);
369 static bool warn_once =
true;
370 if (group_it.
chains_.size() > 1 && warn_once)
373 QMessageBox::warning(
this,
"Group with Multiple Kinematic Chains",
374 "Warning: this MoveIt Setup Assistant is only designed to handle one kinematic chain per "
375 "group. The loaded SRDF has more than one kinematic chain for a group. A possible loss of "
380 for (std::vector<std::pair<std::string, std::string> >::const_iterator chain_it = group_it.
chains_.begin();
381 chain_it != group_it.
chains_.end(); ++chain_it)
383 QTreeWidgetItem* j =
new QTreeWidgetItem(chains);
385 j->setText(0, QString(chain_it->first.c_str()).append(
" -> ").append(chain_it->second.c_str()));
390 QTreeWidgetItem* subgroups =
new QTreeWidgetItem(group);
391 subgroups->setText(0,
"Subgroups");
392 subgroups->setFont(0, type_font);
394 group->addChild(subgroups);
397 for (std::vector<std::string>::iterator subgroup_it = group_it.
subgroups_.begin();
398 subgroup_it != group_it.
subgroups_.end(); ++subgroup_it)
406 if (
group.name_ == *subgroup_it)
408 searched_group = &
group;
414 if (searched_group ==
nullptr)
416 QMessageBox::critical(
this,
"Error Loading SRDF",
417 QString(
"Subgroup '")
418 .
append(subgroup_it->c_str())
419 .append(
"' of group '")
420 .append(group_it.
name_.c_str())
421 .append(
"' not found. Your SRDF is invalid"));
469 switch (plan_group.
type_)
487 QMessageBox::critical(
this,
"Error Loading",
"An internal error has occured while loading.");
500 const moveit::core::RobotModelConstPtr& model =
config_data_->getRobotModel();
503 const std::vector<std::string>& joints = model->getJointModelNames();
507 QMessageBox::critical(
this,
"Error Loading",
"No joints found for robot model");
519 QString(
"Edit '").
append(QString::fromUtf8(this_group->
name_.c_str())).append(
"' Joint Collection"));
531 const moveit::core::RobotModelConstPtr& model =
config_data_->getRobotModel();
534 const std::vector<std::string>& links = model->getLinkModelNames();
538 QMessageBox::critical(
this,
"Error Loading",
"No links found for robot model");
550 QString(
"Edit '").
append(QString::fromUtf8(this_group->
name_.c_str())).append(
"' Link Collection"));
565 if (this_group->
chains_.size() > 1)
567 QMessageBox::warning(
this,
"Multiple Kinematic Chains",
568 "Warning: This setup assistant is only designed to handle "
569 "one kinematic chain per group. The loaded SRDF has more "
570 "than one kinematic chain for a group. A possible loss of "
575 if (!this_group->
chains_.empty())
582 QString(
"Edit '").
append(QString::fromUtf8(this_group->
name_.c_str())).append(
"' Kinematic Chain"));
594 std::vector<std::string> subgroups;
602 subgroups.push_back(
group.name_);
614 QString(
"Edit '").
append(QString::fromUtf8(this_group->
name_.c_str())).append(
"' Subgroups"));
628 if (this_group ==
nullptr)
676 if (QMessageBox::question(
this,
"Confirm Group Deletion",
677 QString(
"Are you sure you want to delete the planning group '")
679 .append(
"'? This will also delete all references in subgroups, robot poses and end "
681 QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel)
687 bool have_confirmed_group_state_deletion =
false;
688 bool have_deleted_group_state =
true;
689 while (have_deleted_group_state)
691 have_deleted_group_state =
false;
692 for (std::vector<srdf::Model::GroupState>::iterator pose_it =
config_data_->srdf_->group_states_.begin();
693 pose_it !=
config_data_->srdf_->group_states_.end(); ++pose_it)
696 if (pose_it->group_ == searched_group->
name_)
698 if (!have_confirmed_group_state_deletion)
700 have_confirmed_group_state_deletion =
true;
703 if (QMessageBox::question(
704 this,
"Confirm Group State Deletion",
705 QString(
"The group that is about to be deleted has robot poses (robot states) that depend on this "
706 "group. Are you sure you want to delete this group as well as all dependend robot poses?"),
707 QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel)
716 have_deleted_group_state =
true;
723 bool have_confirmed_end_effector_deletion =
false;
724 bool have_deleted_end_effector =
true;
725 while (have_deleted_end_effector)
727 have_deleted_end_effector =
false;
728 for (std::vector<srdf::Model::EndEffector>::iterator effector_it =
config_data_->srdf_->end_effectors_.begin();
729 effector_it !=
config_data_->srdf_->end_effectors_.end(); ++effector_it)
732 if (effector_it->component_group_ == searched_group->
name_)
734 if (!have_confirmed_end_effector_deletion)
736 have_confirmed_end_effector_deletion =
true;
739 if (QMessageBox::question(
740 this,
"Confirm End Effector Deletion",
741 QString(
"The group that is about to be deleted has end effectors (grippers) that depend on this "
742 "group. Are you sure you want to delete this group as well as all dependend end effectors?"),
743 QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel)
752 have_deleted_end_effector =
true;
761 for (std::vector<srdf::Model::Group>::iterator group_it =
config_data_->srdf_->groups_.begin();
762 group_it !=
config_data_->srdf_->groups_.end(); ++group_it)
765 if (group_it->
name_ == group)
776 bool deleted_subgroup =
true;
777 while (deleted_subgroup)
779 deleted_subgroup =
false;
782 for (std::vector<std::string>::iterator subgroup_it = group_it.
subgroups_.begin();
783 subgroup_it != group_it.
subgroups_.end(); ++subgroup_it)
786 if (subgroup_it->compare(group) == 0)
789 deleted_subgroup =
true;
829 searched_group->
joints_.clear();
858 searched_group->
links_.clear();
890 if ((!tip.empty() &&
base.empty()) || (tip.empty() && !
base.empty()))
892 QMessageBox::warning(
this,
"Error Saving",
893 "You must specify a link for both the base and tip, or leave both "
899 if (!tip.empty() && !
base.empty())
902 if (tip.compare(base) == 0)
904 QMessageBox::warning(
this,
"Error Saving",
"Tip and base link cannot be the same link.");
908 bool found_tip =
false;
909 bool found_base =
false;
910 const std::vector<std::string>& links =
config_data_->getRobotModel()->getLinkModelNames();
912 for (
const std::string& link : links)
915 if (link.compare(tip) == 0)
917 else if (link.compare(
base) == 0)
921 if (found_tip && found_base)
926 if (!found_tip || !found_base)
928 QMessageBox::warning(
this,
"Error Saving",
"Tip or base link(s) were not found in kinematic chain.");
934 searched_group->
chains_.clear();
937 if (!tip.empty() && !
base.empty())
939 searched_group->
chains_.push_back(std::pair<std::string, std::string>(base, tip));
964 std::map<std::string, int> group_nodes;
971 group_nodes.insert(std::pair<std::string, int>(
group.name_, node_id));
976 typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS> Graph;
977 Graph g(group_nodes.size());
993 int to_id = group_nodes[to_string];
996 add_edge(from_id, to_id, g);
1002 for (
const std::string& to_string :
group.subgroups_)
1006 int to_id = group_nodes[to_string];
1009 add_edge(from_id, to_id, g);
1017 bool has_cycle =
false;
1018 CycleDetector vis(has_cycle);
1019 boost::depth_first_search(g, visitor(vis));
1024 QMessageBox::warning(
this,
"Error Saving",
"Depth first search reveals a cycle in the subgroups");
1062 const std::string& kinematics_parameters_file =
1069 if (group_name.empty())
1071 QMessageBox::warning(
this,
"Error Saving",
"A name must be given for the group!");
1085 if (
group.name_.compare(group_name) == 0)
1088 if (&
group != searched_group)
1090 QMessageBox::warning(
this,
"Error Saving",
"A group already exists with that name!");
1097 double kinematics_resolution_double;
1100 kinematics_resolution_double = boost::lexical_cast<double>(kinematics_resolution);
1102 catch (boost::bad_lexical_cast&)
1104 QMessageBox::warning(
this,
"Error Saving",
"Unable to convert kinematics resolution to a double number.");
1109 double kinematics_timeout_double;
1112 kinematics_timeout_double = boost::lexical_cast<double>(kinematics_timeout);
1114 catch (boost::bad_lexical_cast&)
1116 QMessageBox::warning(
this,
"Error Saving",
"Unable to convert kinematics solver timeout to a double number.");
1121 if (kinematics_resolution_double <= 0)
1123 QMessageBox::warning(
this,
"Error Saving",
"Kinematics solver search resolution must be greater than 0.");
1126 if (kinematics_timeout_double <= 0)
1128 QMessageBox::warning(
this,
"Error Saving",
"Kinematics solver search timeout must be greater than 0.");
1131 if (goal_joint_tolerance <= 0)
1133 QMessageBox::warning(
this,
"Error Saving",
"Goal joint tolerance must be greater than 0.");
1136 if (goal_position_tolerance <= 0)
1138 QMessageBox::warning(
this,
"Error Saving",
"Goal position tolerance must be greater than 0.");
1141 if (goal_orientation_tolerance <= 0)
1143 QMessageBox::warning(
this,
"Error Saving",
"Goal orientation tolerance must be greater than 0.");
1150 if (searched_group ==
nullptr)
1153 new_group.
name_ = group_name;
1161 const std::string old_group_name = searched_group->
name_;
1164 searched_group->
name_ = group_name;
1168 for (std::vector<srdf::Model::Group>::iterator group_it =
config_data_->srdf_->groups_.begin();
1169 group_it !=
config_data_->srdf_->groups_.end(); ++group_it)
1172 for (std::string& subgroup : group_it->
subgroups_)
1175 if (subgroup.compare(old_group_name) == 0)
1177 subgroup.assign(group_name);
1184 for (std::vector<srdf::Model::EndEffector>::iterator eef_it =
config_data_->srdf_->end_effectors_.begin();
1185 eef_it !=
config_data_->srdf_->end_effectors_.end(); ++eef_it)
1188 if (eef_it->parent_group_.compare(old_group_name) == 0)
1191 "Changed eef '" << eef_it->name_ <<
"' to new parent group name " << group_name);
1192 eef_it->parent_group_ = group_name;
1197 if (eef_it->component_group_.compare(old_group_name) == 0)
1200 "Changed eef '" << eef_it->name_ <<
"' to new group name " << group_name);
1201 eef_it->component_group_ = group_name;
1207 for (std::vector<srdf::Model::GroupState>::iterator state_it =
config_data_->srdf_->group_states_.begin();
1208 state_it !=
config_data_->srdf_->group_states_.end(); ++state_it)
1211 if (state_it->group_.compare(old_group_name) == 0)
1214 <<
"' to new parent group name "
1216 state_it->group_ = group_name;
1226 config_data_->group_meta_data_[group_name].kinematics_solver_ = kinematics_solver;
1227 config_data_->group_meta_data_[group_name].kinematics_solver_search_resolution_ = kinematics_resolution_double;
1228 config_data_->group_meta_data_[group_name].kinematics_solver_timeout_ = kinematics_timeout_double;
1229 config_data_->group_meta_data_[group_name].goal_joint_tolerance_ = goal_joint_tolerance;
1230 config_data_->group_meta_data_[group_name].goal_position_tolerance_ = goal_position_tolerance;
1231 config_data_->group_meta_data_[group_name].goal_orientation_tolerance_ = goal_orientation_tolerance;
1232 config_data_->group_meta_data_[group_name].kinematics_parameters_file_ = kinematics_parameters_file;
1233 config_data_->group_meta_data_[group_name].default_planner_ = (default_planner ==
"None" ?
"" : default_planner);
1340 if (editing && editing->
joints_.empty() && editing->
links_.empty() && editing->
chains_.empty() &&
1344 for (std::vector<srdf::Model::Group>::iterator group_it =
config_data_->srdf_->groups_.begin();
1345 group_it !=
config_data_->srdf_->groups_.end(); ++group_it)
1346 if (&(*group_it) == editing)
1378 if (link.contains(
"expand"))
1414 for (
const std::string& link : links)
1434 for (
const std::string& joint : joints)
1465 for (
const std::string&
group : groups)
1481 : group_(
group), type_(type)