00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037 #include <moveit/motion_planning_rviz_plugin/motion_planning_frame.h>
00038 #include <moveit/motion_planning_rviz_plugin/motion_planning_display.h>
00039 #include <moveit/move_group/capability_names.h>
00040
00041 #include <geometric_shapes/shape_operations.h>
00042
00043 #include <rviz/display_context.h>
00044 #include <rviz/frame_manager.h>
00045
00046 #include <std_srvs/Empty.h>
00047
00048 #include <QMessageBox>
00049 #include <QInputDialog>
00050 #include <QShortcut>
00051
00052 #include "ui_motion_planning_rviz_plugin_frame.h"
00053
00054 namespace moveit_rviz_plugin
00055 {
00056 MotionPlanningFrame::MotionPlanningFrame(MotionPlanningDisplay* pdisplay, rviz::DisplayContext* context,
00057 QWidget* parent)
00058 : QWidget(parent)
00059 , planning_display_(pdisplay)
00060 , context_(context)
00061 , ui_(new Ui::MotionPlanningUI())
00062 , first_time_(true)
00063 , clear_octomap_service_client_(nh_.serviceClient<std_srvs::Empty>(move_group::CLEAR_OCTOMAP_SERVICE_NAME))
00064 {
00065
00066 ui_->setupUi(this);
00067
00068
00069
00070 connect(ui_->plan_button, SIGNAL(clicked()), this, SLOT(planButtonClicked()));
00071 connect(ui_->execute_button, SIGNAL(clicked()), this, SLOT(executeButtonClicked()));
00072 connect(ui_->plan_and_execute_button, SIGNAL(clicked()), this, SLOT(planAndExecuteButtonClicked()));
00073 connect(ui_->stop_button, SIGNAL(clicked()), this, SLOT(stopButtonClicked()));
00074 connect(ui_->use_start_state_button, SIGNAL(clicked()), this, SLOT(useStartStateButtonClicked()));
00075 connect(ui_->use_goal_state_button, SIGNAL(clicked()), this, SLOT(useGoalStateButtonClicked()));
00076 connect(ui_->database_connect_button, SIGNAL(clicked()), this, SLOT(databaseConnectButtonClicked()));
00077 connect(ui_->save_scene_button, SIGNAL(clicked()), this, SLOT(saveSceneButtonClicked()));
00078 connect(ui_->save_query_button, SIGNAL(clicked()), this, SLOT(saveQueryButtonClicked()));
00079 connect(ui_->delete_scene_button, SIGNAL(clicked()), this, SLOT(deleteSceneButtonClicked()));
00080 connect(ui_->delete_query_button, SIGNAL(clicked()), this, SLOT(deleteQueryButtonClicked()));
00081 connect(ui_->planning_scene_tree, SIGNAL(itemSelectionChanged()), this, SLOT(planningSceneItemClicked()));
00082 connect(ui_->load_scene_button, SIGNAL(clicked()), this, SLOT(loadSceneButtonClicked()));
00083 connect(ui_->load_query_button, SIGNAL(clicked()), this, SLOT(loadQueryButtonClicked()));
00084 connect(ui_->allow_looking, SIGNAL(toggled(bool)), this, SLOT(allowLookingToggled(bool)));
00085 connect(ui_->allow_replanning, SIGNAL(toggled(bool)), this, SLOT(allowReplanningToggled(bool)));
00086 connect(ui_->allow_external_program, SIGNAL(toggled(bool)), this, SLOT(allowExternalProgramCommunication(bool)));
00087 connect(ui_->planning_algorithm_combo_box, SIGNAL(currentIndexChanged(int)), this,
00088 SLOT(planningAlgorithmIndexChanged(int)));
00089 connect(ui_->import_file_button, SIGNAL(clicked()), this, SLOT(importFileButtonClicked()));
00090 connect(ui_->import_url_button, SIGNAL(clicked()), this, SLOT(importUrlButtonClicked()));
00091 connect(ui_->clear_scene_button, SIGNAL(clicked()), this, SLOT(clearSceneButtonClicked()));
00092 connect(ui_->scene_scale, SIGNAL(valueChanged(int)), this, SLOT(sceneScaleChanged(int)));
00093 connect(ui_->scene_scale, SIGNAL(sliderPressed()), this, SLOT(sceneScaleStartChange()));
00094 connect(ui_->scene_scale, SIGNAL(sliderReleased()), this, SLOT(sceneScaleEndChange()));
00095 connect(ui_->remove_object_button, SIGNAL(clicked()), this, SLOT(removeObjectButtonClicked()));
00096 connect(ui_->object_x, SIGNAL(valueChanged(double)), this, SLOT(objectPoseValueChanged(double)));
00097 connect(ui_->object_y, SIGNAL(valueChanged(double)), this, SLOT(objectPoseValueChanged(double)));
00098 connect(ui_->object_z, SIGNAL(valueChanged(double)), this, SLOT(objectPoseValueChanged(double)));
00099 connect(ui_->object_rx, SIGNAL(valueChanged(double)), this, SLOT(objectPoseValueChanged(double)));
00100 connect(ui_->object_ry, SIGNAL(valueChanged(double)), this, SLOT(objectPoseValueChanged(double)));
00101 connect(ui_->object_rz, SIGNAL(valueChanged(double)), this, SLOT(objectPoseValueChanged(double)));
00102 connect(ui_->publish_current_scene_button, SIGNAL(clicked()), this, SLOT(publishSceneButtonClicked()));
00103 connect(ui_->collision_objects_list, SIGNAL(itemSelectionChanged()), this, SLOT(selectedCollisionObjectChanged()));
00104 connect(ui_->collision_objects_list, SIGNAL(itemChanged(QListWidgetItem*)), this,
00105 SLOT(collisionObjectChanged(QListWidgetItem*)));
00106 connect(ui_->path_constraints_combo_box, SIGNAL(currentIndexChanged(int)), this,
00107 SLOT(pathConstraintsIndexChanged(int)));
00108 connect(ui_->clear_octomap_button, SIGNAL(clicked()), this, SLOT(onClearOctomapClicked()));
00109 connect(ui_->planning_scene_tree, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this,
00110 SLOT(warehouseItemNameChanged(QTreeWidgetItem*, int)));
00111 connect(ui_->reset_db_button, SIGNAL(clicked()), this, SLOT(resetDbButtonClicked()));
00112 connect(ui_->export_scene_text_button, SIGNAL(clicked()), this, SLOT(exportAsTextButtonClicked()));
00113 connect(ui_->import_scene_text_button, SIGNAL(clicked()), this, SLOT(importFromTextButtonClicked()));
00114 connect(ui_->load_state_button, SIGNAL(clicked()), this, SLOT(loadStateButtonClicked()));
00115 connect(ui_->save_start_state_button, SIGNAL(clicked()), this, SLOT(saveStartStateButtonClicked()));
00116 connect(ui_->save_goal_state_button, SIGNAL(clicked()), this, SLOT(saveGoalStateButtonClicked()));
00117 connect(ui_->set_as_start_state_button, SIGNAL(clicked()), this, SLOT(setAsStartStateButtonClicked()));
00118 connect(ui_->set_as_goal_state_button, SIGNAL(clicked()), this, SLOT(setAsGoalStateButtonClicked()));
00119 connect(ui_->remove_state_button, SIGNAL(clicked()), this, SLOT(removeStateButtonClicked()));
00120 connect(ui_->clear_states_button, SIGNAL(clicked()), this, SLOT(clearStatesButtonClicked()));
00121 connect(ui_->approximate_ik, SIGNAL(stateChanged(int)), this, SLOT(approximateIKChanged(int)));
00122
00123 connect(ui_->detect_objects_button, SIGNAL(clicked()), this, SLOT(detectObjectsButtonClicked()));
00124 connect(ui_->pick_button, SIGNAL(clicked()), this, SLOT(pickObjectButtonClicked()));
00125 connect(ui_->place_button, SIGNAL(clicked()), this, SLOT(placeObjectButtonClicked()));
00126 connect(ui_->detected_objects_list, SIGNAL(itemSelectionChanged()), this, SLOT(selectedDetectedObjectChanged()));
00127 connect(ui_->detected_objects_list, SIGNAL(itemChanged(QListWidgetItem*)), this,
00128 SLOT(detectedObjectChanged(QListWidgetItem*)));
00129 connect(ui_->support_surfaces_list, SIGNAL(itemSelectionChanged()), this, SLOT(selectedSupportSurfaceChanged()));
00130
00131 connect(ui_->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int)));
00132
00133 QShortcut* copy_object_shortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_C), ui_->collision_objects_list);
00134 connect(copy_object_shortcut, SIGNAL(activated()), this, SLOT(copySelectedCollisionObject()));
00135
00136 ui_->reset_db_button->hide();
00137 ui_->background_job_progress->hide();
00138 ui_->background_job_progress->setMaximum(0);
00139
00140 ui_->tabWidget->setCurrentIndex(0);
00141
00142 known_collision_objects_version_ = 0;
00143
00144 planning_scene_publisher_ = nh_.advertise<moveit_msgs::PlanningScene>("planning_scene", 1);
00145 planning_scene_world_publisher_ = nh_.advertise<moveit_msgs::PlanningSceneWorld>("planning_scene_world", 1);
00146
00147
00148 object_recognition_client_.reset(new actionlib::SimpleActionClient<object_recognition_msgs::ObjectRecognitionAction>(
00149 OBJECT_RECOGNITION_ACTION, false));
00150 object_recognition_subscriber_ =
00151 nh_.subscribe("recognized_object_array", 1, &MotionPlanningFrame::listenDetectedObjects, this);
00152
00153 if (object_recognition_client_)
00154 {
00155 try
00156 {
00157 waitForAction(object_recognition_client_, nh_, ros::Duration(3.0), OBJECT_RECOGNITION_ACTION);
00158 }
00159 catch (std::runtime_error& ex)
00160 {
00161
00162 object_recognition_client_.reset();
00163 }
00164 }
00165 try
00166 {
00167 planning_scene_interface_.reset(new moveit::planning_interface::PlanningSceneInterface());
00168 }
00169 catch (std::runtime_error& ex)
00170 {
00171 ROS_ERROR("%s", ex.what());
00172 }
00173
00174 try
00175 {
00176 const planning_scene_monitor::LockedPlanningSceneRO& ps = planning_display_->getPlanningSceneRO();
00177 if (ps)
00178 {
00179 semantic_world_.reset(new moveit::semantic_world::SemanticWorld(ps));
00180 }
00181 else
00182 semantic_world_.reset();
00183 if (semantic_world_)
00184 {
00185 semantic_world_->addTableCallback(boost::bind(&MotionPlanningFrame::updateTables, this));
00186 }
00187 }
00188 catch (std::runtime_error& ex)
00189 {
00190 ROS_ERROR("%s", ex.what());
00191 }
00192 }
00193
00194 MotionPlanningFrame::~MotionPlanningFrame()
00195 {
00196 }
00197
00198 void MotionPlanningFrame::approximateIKChanged(int state)
00199 {
00200 planning_display_->useApproximateIK(state == Qt::Checked);
00201 }
00202
00203 void MotionPlanningFrame::setItemSelectionInList(const std::string& item_name, bool selection, QListWidget* list)
00204 {
00205 QList<QListWidgetItem*> found_items = list->findItems(QString(item_name.c_str()), Qt::MatchExactly);
00206 for (std::size_t i = 0; i < found_items.size(); ++i)
00207 found_items[i]->setSelected(selection);
00208 }
00209
00210 void MotionPlanningFrame::allowExternalProgramCommunication(bool enable)
00211 {
00212 planning_display_->getRobotInteraction()->toggleMoveInteractiveMarkerTopic(enable);
00213 planning_display_->toggleSelectPlanningGroupSubscription(enable);
00214 if (enable)
00215 {
00216 ros::NodeHandle nh;
00217 plan_subscriber_ = nh.subscribe("/rviz/moveit/plan", 1, &MotionPlanningFrame::remotePlanCallback, this);
00218 execute_subscriber_ = nh.subscribe("/rviz/moveit/execute", 1, &MotionPlanningFrame::remoteExecuteCallback, this);
00219 update_start_state_subscriber_ =
00220 nh.subscribe("/rviz/moveit/update_start_state", 1, &MotionPlanningFrame::remoteUpdateStartStateCallback, this);
00221 update_goal_state_subscriber_ =
00222 nh.subscribe("/rviz/moveit/update_goal_state", 1, &MotionPlanningFrame::remoteUpdateGoalStateCallback, this);
00223 }
00224 else
00225 {
00226 plan_subscriber_.shutdown();
00227 execute_subscriber_.shutdown();
00228 update_start_state_subscriber_.shutdown();
00229 update_goal_state_subscriber_.shutdown();
00230 }
00231 }
00232
00233 void MotionPlanningFrame::fillStateSelectionOptions()
00234 {
00235 ui_->start_state_selection->clear();
00236 ui_->goal_state_selection->clear();
00237
00238 if (!planning_display_->getPlanningSceneMonitor())
00239 return;
00240
00241 const robot_model::RobotModelConstPtr& kmodel = planning_display_->getRobotModel();
00242 std::string group = planning_display_->getCurrentPlanningGroup();
00243 if (group.empty())
00244 return;
00245 const robot_model::JointModelGroup* jmg = kmodel->getJointModelGroup(group);
00246 if (jmg)
00247 {
00248 ui_->start_state_selection->addItem(QString("<random valid>"));
00249 ui_->start_state_selection->addItem(QString("<random>"));
00250 ui_->start_state_selection->addItem(QString("<current>"));
00251 ui_->start_state_selection->addItem(QString("<same as goal>"));
00252
00253 ui_->goal_state_selection->addItem(QString("<random valid>"));
00254 ui_->goal_state_selection->addItem(QString("<random>"));
00255 ui_->goal_state_selection->addItem(QString("<current>"));
00256 ui_->goal_state_selection->addItem(QString("<same as start>"));
00257
00258 const std::vector<std::string>& known_states = jmg->getDefaultStateNames();
00259 if (!known_states.empty())
00260 {
00261 ui_->start_state_selection->insertSeparator(ui_->start_state_selection->count());
00262 ui_->goal_state_selection->insertSeparator(ui_->goal_state_selection->count());
00263 for (std::size_t i = 0; i < known_states.size(); ++i)
00264 {
00265 ui_->start_state_selection->addItem(QString::fromStdString(known_states[i]));
00266 ui_->goal_state_selection->addItem(QString::fromStdString(known_states[i]));
00267 }
00268 }
00269 ui_->start_state_selection->setCurrentIndex(2);
00270 ui_->goal_state_selection->setCurrentIndex(0);
00271 }
00272 }
00273
00274 void MotionPlanningFrame::changePlanningGroupHelper()
00275 {
00276 if (!planning_display_->getPlanningSceneMonitor())
00277 return;
00278
00279 planning_display_->addMainLoopJob(boost::bind(&MotionPlanningFrame::fillStateSelectionOptions, this));
00280 planning_display_->addMainLoopJob(
00281 boost::bind(&MotionPlanningFrame::populateConstraintsList, this, std::vector<std::string>()));
00282
00283 const robot_model::RobotModelConstPtr& kmodel = planning_display_->getRobotModel();
00284 std::string group = planning_display_->getCurrentPlanningGroup();
00285
00286 if (!group.empty() && kmodel)
00287 {
00288 if (move_group_ && move_group_->getName() == group)
00289 return;
00290 ROS_INFO("Constructing new MoveGroup connection for group '%s' in namespace '%s'", group.c_str(),
00291 planning_display_->getMoveGroupNS().c_str());
00292 moveit::planning_interface::MoveGroup::Options opt(group);
00293 opt.robot_model_ = kmodel;
00294 opt.robot_description_.clear();
00295 opt.node_handle_ = ros::NodeHandle(planning_display_->getMoveGroupNS());
00296 try
00297 {
00298 move_group_.reset(new moveit::planning_interface::MoveGroup(opt, context_->getFrameManager()->getTFClientPtr(),
00299 ros::WallDuration(30, 0)));
00300 if (planning_scene_storage_)
00301 move_group_->setConstraintsDatabase(ui_->database_host->text().toStdString(), ui_->database_port->value());
00302 }
00303 catch (std::runtime_error& ex)
00304 {
00305 ROS_ERROR("%s", ex.what());
00306 }
00307 if (move_group_)
00308 {
00309 move_group_->allowLooking(ui_->allow_looking->isChecked());
00310 move_group_->allowReplanning(ui_->allow_replanning->isChecked());
00311 moveit_msgs::PlannerInterfaceDescription desc;
00312 if (move_group_->getInterfaceDescription(desc))
00313 planning_display_->addMainLoopJob(boost::bind(&MotionPlanningFrame::populatePlannersList, this, desc));
00314 planning_display_->addBackgroundJob(boost::bind(&MotionPlanningFrame::populateConstraintsList, this),
00315 "populateConstraintsList");
00316
00317 if (first_time_)
00318 {
00319 first_time_ = false;
00320 const planning_scene_monitor::LockedPlanningSceneRO& ps = planning_display_->getPlanningSceneRO();
00321 if (ps)
00322 {
00323 planning_display_->setQueryStartState(ps->getCurrentState());
00324 planning_display_->setQueryGoalState(ps->getCurrentState());
00325 }
00326 }
00327 }
00328 }
00329 }
00330
00331 void MotionPlanningFrame::changePlanningGroup()
00332 {
00333 planning_display_->addBackgroundJob(boost::bind(&MotionPlanningFrame::changePlanningGroupHelper, this),
00334 "Frame::changePlanningGroup");
00335 }
00336
00337 void MotionPlanningFrame::sceneUpdate(planning_scene_monitor::PlanningSceneMonitor::SceneUpdateType update_type)
00338 {
00339 if (update_type & planning_scene_monitor::PlanningSceneMonitor::UPDATE_GEOMETRY)
00340 planning_display_->addMainLoopJob(boost::bind(&MotionPlanningFrame::populateCollisionObjectsList, this));
00341 }
00342
00343 void MotionPlanningFrame::importResource(const std::string& path)
00344 {
00345 if (planning_display_->getPlanningSceneMonitor())
00346 {
00347 shapes::Mesh* mesh = shapes::createMeshFromResource(path);
00348 if (mesh)
00349 {
00350 std::size_t slash = path.find_last_of("/\\");
00351 std::string name = path.substr(slash + 1);
00352 shapes::ShapeConstPtr shape(mesh);
00353 Eigen::Affine3d pose;
00354 pose.setIdentity();
00355
00356 if (planning_display_->getPlanningSceneRO()->getCurrentState().hasAttachedBody(name))
00357 {
00358 QMessageBox::warning(this, QString("Duplicate names"), QString("An attached object named '")
00359 .append(name.c_str())
00360 .append("' already exists. Please rename the "
00361 "attached object before importing."));
00362 return;
00363 }
00364
00365
00366 if (planning_display_->getPlanningSceneRO()->getWorld()->hasObject(name))
00367 {
00368 QMessageBox msgBox;
00369 msgBox.setText("There exists another object with the same name.");
00370 msgBox.setInformativeText("Would you like to overwrite it?");
00371 msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
00372 msgBox.setDefaultButton(QMessageBox::No);
00373 int ret = msgBox.exec();
00374
00375 switch (ret)
00376 {
00377 case QMessageBox::Yes:
00378
00379 {
00380 planning_scene_monitor::LockedPlanningSceneRW ps = planning_display_->getPlanningSceneRW();
00381 if (ps)
00382 {
00383 ps->getWorldNonConst()->removeObject(name);
00384 addObject(ps->getWorldNonConst(), name, shape, pose);
00385 }
00386 }
00387 break;
00388 case QMessageBox::No:
00389 {
00390
00391 bool ok = false;
00392 QString text = QInputDialog::getText(
00393 this, tr("Choose a new name"), tr("Import the new object under the name:"), QLineEdit::Normal,
00394 QString::fromStdString(name + "-" + boost::lexical_cast<std::string>(
00395 planning_display_->getPlanningSceneRO()->getWorld()->size())),
00396 &ok);
00397 if (ok)
00398 {
00399 if (!text.isEmpty())
00400 {
00401 name = text.toStdString();
00402 planning_scene_monitor::LockedPlanningSceneRW ps = planning_display_->getPlanningSceneRW();
00403 if (ps)
00404 {
00405 if (ps->getWorld()->hasObject(name))
00406 QMessageBox::warning(
00407 this, "Name already exists",
00408 QString("The name '").append(name.c_str()).append("' already exists. Not importing object."));
00409 else
00410 addObject(ps->getWorldNonConst(), name, shape, pose);
00411 }
00412 }
00413 else
00414 QMessageBox::warning(this, "Object not imported", "Cannot use an empty name for an imported object");
00415 }
00416 break;
00417 }
00418 default:
00419
00420 break;
00421 }
00422 }
00423 else
00424 {
00425 planning_scene_monitor::LockedPlanningSceneRW ps = planning_display_->getPlanningSceneRW();
00426 if (ps)
00427 addObject(ps->getWorldNonConst(), name, shape, pose);
00428 }
00429 }
00430 else
00431 {
00432 QMessageBox::warning(this, QString("Import error"), QString("Unable to import scene"));
00433 return;
00434 }
00435 }
00436 }
00437
00438 void MotionPlanningFrame::enable()
00439 {
00440 ui_->planning_algorithm_combo_box->clear();
00441 ui_->library_label->setText("NO PLANNING LIBRARY LOADED");
00442 ui_->library_label->setStyleSheet("QLabel { color : red; font: bold }");
00443 ui_->object_status->setText("");
00444
00445
00446 parentWidget()->show();
00447 show();
00448 }
00449
00450 void MotionPlanningFrame::disable()
00451 {
00452 move_group_.reset();
00453 parentWidget()->hide();
00454 hide();
00455 }
00456
00457 void MotionPlanningFrame::tabChanged(int index)
00458 {
00459 if (scene_marker_ && ui_->tabWidget->tabText(index).toStdString() != TAB_OBJECTS)
00460 scene_marker_.reset();
00461 else if (ui_->tabWidget->tabText(index).toStdString() == TAB_OBJECTS)
00462 selectedCollisionObjectChanged();
00463 }
00464
00465 void MotionPlanningFrame::updateSceneMarkers(float wall_dt, float ros_dt)
00466 {
00467 if (scene_marker_)
00468 scene_marker_->update(wall_dt);
00469 }
00470
00471 void MotionPlanningFrame::updateExternalCommunication()
00472 {
00473 if (ui_->allow_external_program->isChecked())
00474 {
00475 planning_display_->getRobotInteraction()->toggleMoveInteractiveMarkerTopic(true);
00476 }
00477 }
00478
00479 }