moveit2
The MoveIt Motion Planning Framework for ROS 2.
Loading...
Searching...
No Matches
motion_planning_frame.cpp
Go to the documentation of this file.
1/*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2012, Willow Garage, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 * * Neither the name of Willow Garage nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *********************************************************************/
34
35/* Author: Ioan Sucan */
36
37#include <functional>
38
39#include <rclcpp/version.h>
40
47
48#include <geometric_shapes/shape_operations.h>
49
50#include <rviz_common/display_context.hpp>
51#include <rviz_common/frame_manager_iface.hpp>
52// For Rolling, Kilted, and newer
53#if RCLCPP_VERSION_GTE(29, 6, 0)
54#include <tf2_ros/buffer.hpp>
55// For Jazzy and older
56#else
57#include <tf2_ros/buffer.h>
58#endif
59
60#include <std_srvs/srv/empty.hpp>
61
62#include <QMessageBox>
63#include <QInputDialog>
64#include <QFileDialog>
65#include <QComboBox>
66#include <QShortcut>
67
68#include "ui_motion_planning_rviz_plugin_frame.h"
69
70#include <cmath>
71
72namespace moveit_rviz_plugin
73{
74
75MotionPlanningFrame::MotionPlanningFrame(MotionPlanningDisplay* pdisplay, rviz_common::DisplayContext* context,
76 QWidget* parent)
77 : QWidget(parent)
78 , planning_display_(pdisplay)
79 , context_(context)
80 , ui_(new Ui::MotionPlanningUI())
81 , logger_(moveit::getLogger("moveit.ros.motion_planning_frame"))
82 , first_time_(true)
83{
84 auto ros_node_abstraction = context_->getRosNodeAbstraction().lock();
85 if (!ros_node_abstraction)
86 {
87 RCLCPP_INFO(logger_, "Unable to lock weak_ptr from DisplayContext in MotionPlanningFrame constructor");
88 return;
89 }
90 node_ = ros_node_abstraction->get_raw_node();
91
92 // Prepare database parameters
93 if (!node_->has_parameter("warehouse_host"))
94 node_->declare_parameter<std::string>("warehouse_host", "127.0.0.1");
95 if (!node_->has_parameter("warehouse_plugin"))
96 node_->declare_parameter<std::string>("warehouse_plugin", "warehouse_ros_mongo::MongoDatabaseConnection");
97 if (!node_->has_parameter("warehouse_port"))
98 node_->declare_parameter<int>("warehouse_port", 33829);
99
100 // set up the GUI
101 ui_->setupUi(this);
102 ui_->shapes_combo_box->addItem("Box", shapes::BOX);
103 ui_->shapes_combo_box->addItem("Sphere", shapes::SPHERE);
104 ui_->shapes_combo_box->addItem("Cylinder", shapes::CYLINDER);
105 ui_->shapes_combo_box->addItem("Cone", shapes::CONE);
106 ui_->shapes_combo_box->addItem("Mesh from file", shapes::MESH);
107 ui_->shapes_combo_box->addItem("Mesh from URL", shapes::MESH);
108 setLocalSceneEdited(false);
109
110 // add more tabs
112 ui_->tabWidget->insertTab(2, joints_tab_, "Joints");
113 connect(planning_display_, SIGNAL(queryStartStateChanged()), joints_tab_, SLOT(queryStartStateChanged()));
114 connect(planning_display_, SIGNAL(queryGoalStateChanged()), joints_tab_, SLOT(queryGoalStateChanged()));
115
116 // connect buttons to actions; each action usually registers the function pointer for the actual computation,
117 // to keep the GUI more responsive (using the background job processing)
118 connect(ui_->plan_button, SIGNAL(clicked()), this, SLOT(planButtonClicked()));
119 connect(ui_->execute_button, SIGNAL(clicked()), this, SLOT(executeButtonClicked()));
120 connect(ui_->plan_and_execute_button, SIGNAL(clicked()), this, SLOT(planAndExecuteButtonClicked()));
121 connect(ui_->stop_button, SIGNAL(clicked()), this, SLOT(stopButtonClicked()));
122 connect(ui_->start_state_combo_box, SIGNAL(activated(QString)), this, SLOT(startStateTextChanged(QString)));
123 connect(ui_->goal_state_combo_box, SIGNAL(activated(QString)), this, SLOT(goalStateTextChanged(QString)));
124 connect(ui_->planning_group_combo_box, SIGNAL(currentIndexChanged(QString)), this,
125 SLOT(planningGroupTextChanged(QString)));
126 connect(ui_->database_connect_button, SIGNAL(clicked()), this, SLOT(databaseConnectButtonClicked()));
127 connect(ui_->save_scene_button, SIGNAL(clicked()), this, SLOT(saveSceneButtonClicked()));
128 connect(ui_->save_query_button, SIGNAL(clicked()), this, SLOT(saveQueryButtonClicked()));
129 connect(ui_->delete_scene_button, SIGNAL(clicked()), this, SLOT(deleteSceneButtonClicked()));
130 connect(ui_->delete_query_button, SIGNAL(clicked()), this, SLOT(deleteQueryButtonClicked()));
131 connect(ui_->planning_scene_tree, SIGNAL(itemSelectionChanged()), this, SLOT(planningSceneItemClicked()));
132 connect(ui_->load_scene_button, SIGNAL(clicked()), this, SLOT(loadSceneButtonClicked()));
133 connect(ui_->load_query_button, SIGNAL(clicked()), this, SLOT(loadQueryButtonClicked()));
134 connect(ui_->allow_looking, SIGNAL(toggled(bool)), this, SLOT(allowLookingToggled(bool)));
135 connect(ui_->allow_replanning, SIGNAL(toggled(bool)), this, SLOT(allowReplanningToggled(bool)));
136 connect(ui_->allow_external_program, SIGNAL(toggled(bool)), this, SLOT(allowExternalProgramCommunication(bool)));
137 connect(ui_->planning_pipeline_combo_box, SIGNAL(currentIndexChanged(int)), this,
138 SLOT(planningPipelineIndexChanged(int)));
139 connect(ui_->planning_algorithm_combo_box, SIGNAL(currentIndexChanged(int)), this,
140 SLOT(planningAlgorithmIndexChanged(int)));
141 connect(ui_->clear_scene_button, SIGNAL(clicked()), this, SLOT(clearScene()));
142 connect(ui_->scene_scale, SIGNAL(valueChanged(int)), this, SLOT(sceneScaleChanged(int)));
143 connect(ui_->scene_scale, SIGNAL(sliderPressed()), this, SLOT(sceneScaleStartChange()));
144 connect(ui_->scene_scale, SIGNAL(sliderReleased()), this, SLOT(sceneScaleEndChange()));
145 connect(ui_->remove_object_button, SIGNAL(clicked()), this, SLOT(removeSceneObject()));
146 connect(ui_->object_x, SIGNAL(valueChanged(double)), this, SLOT(objectPoseValueChanged(double)));
147 connect(ui_->object_y, SIGNAL(valueChanged(double)), this, SLOT(objectPoseValueChanged(double)));
148 connect(ui_->object_z, SIGNAL(valueChanged(double)), this, SLOT(objectPoseValueChanged(double)));
149 connect(ui_->object_rx, SIGNAL(valueChanged(double)), this, SLOT(objectPoseValueChanged(double)));
150 connect(ui_->object_ry, SIGNAL(valueChanged(double)), this, SLOT(objectPoseValueChanged(double)));
151 connect(ui_->object_rz, SIGNAL(valueChanged(double)), this, SLOT(objectPoseValueChanged(double)));
152 connect(ui_->publish_current_scene_button, SIGNAL(clicked()), this, SLOT(publishScene()));
153 connect(ui_->collision_objects_list, SIGNAL(itemSelectionChanged()), this, SLOT(selectedCollisionObjectChanged()));
154 connect(ui_->collision_objects_list, SIGNAL(itemChanged(QListWidgetItem*)), this,
155 SLOT(collisionObjectChanged(QListWidgetItem*)));
156 connect(ui_->path_constraints_combo_box, SIGNAL(currentIndexChanged(int)), this,
157 SLOT(pathConstraintsIndexChanged(int)));
158 connect(ui_->clear_octomap_button, SIGNAL(clicked()), this, SLOT(onClearOctomapClicked()));
159 connect(ui_->planning_scene_tree, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this,
160 SLOT(warehouseItemNameChanged(QTreeWidgetItem*, int)));
161 connect(ui_->reset_db_button, SIGNAL(clicked()), this, SLOT(resetDbButtonClicked()));
162
163 connect(ui_->add_object_button, &QPushButton::clicked, this, &MotionPlanningFrame::addSceneObject);
164 connect(ui_->shapes_combo_box, &QComboBox::currentTextChanged, this, &MotionPlanningFrame::shapesComboBoxChanged);
165 connect(ui_->export_scene_geometry_text_button, SIGNAL(clicked()), this, SLOT(exportGeometryAsTextButtonClicked()));
166 connect(ui_->import_scene_geometry_text_button, SIGNAL(clicked()), this, SLOT(importGeometryFromTextButtonClicked()));
167 connect(ui_->load_state_button, SIGNAL(clicked()), this, SLOT(loadStateButtonClicked()));
168 connect(ui_->save_start_state_button, SIGNAL(clicked()), this, SLOT(saveStartStateButtonClicked()));
169 connect(ui_->save_goal_state_button, SIGNAL(clicked()), this, SLOT(saveGoalStateButtonClicked()));
170 connect(ui_->set_as_start_state_button, SIGNAL(clicked()), this, SLOT(setAsStartStateButtonClicked()));
171 connect(ui_->set_as_goal_state_button, SIGNAL(clicked()), this, SLOT(setAsGoalStateButtonClicked()));
172 connect(ui_->remove_state_button, SIGNAL(clicked()), this, SLOT(removeStateButtonClicked()));
173 connect(ui_->clear_states_button, SIGNAL(clicked()), this, SLOT(clearStatesButtonClicked()));
174 connect(ui_->approximate_ik, SIGNAL(stateChanged(int)), this, SLOT(approximateIKChanged(int)));
175
176 connect(ui_->detect_objects_button, SIGNAL(clicked()), this, SLOT(detectObjectsButtonClicked()));
177 connect(ui_->pick_button, SIGNAL(clicked()), this, SLOT(pickObjectButtonClicked()));
178 connect(ui_->place_button, SIGNAL(clicked()), this, SLOT(placeObjectButtonClicked()));
179 connect(ui_->detected_objects_list, SIGNAL(itemSelectionChanged()), this, SLOT(selectedDetectedObjectChanged()));
180 connect(ui_->detected_objects_list, SIGNAL(itemChanged(QListWidgetItem*)), this,
181 SLOT(detectedObjectChanged(QListWidgetItem*)));
182 connect(ui_->support_surfaces_list, SIGNAL(itemSelectionChanged()), this, SLOT(selectedSupportSurfaceChanged()));
183
184 connect(ui_->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int)));
185
186 /* Notice changes to be safed in config file */
187 connect(ui_->database_host, SIGNAL(textChanged(QString)), this, SIGNAL(configChanged()));
188 connect(ui_->database_port, SIGNAL(valueChanged(int)), this, SIGNAL(configChanged()));
189
190 connect(ui_->planning_time, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
191 connect(ui_->planning_attempts, SIGNAL(valueChanged(int)), this, SIGNAL(configChanged()));
192 connect(ui_->velocity_scaling_factor, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
193 connect(ui_->acceleration_scaling_factor, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
194
195 connect(ui_->allow_replanning, SIGNAL(stateChanged(int)), this, SIGNAL(configChanged()));
196 connect(ui_->allow_looking, SIGNAL(stateChanged(int)), this, SIGNAL(configChanged()));
197 connect(ui_->allow_external_program, SIGNAL(stateChanged(int)), this, SIGNAL(configChanged()));
198 connect(ui_->use_cartesian_path, SIGNAL(stateChanged(int)), this, SIGNAL(configChanged()));
199 connect(ui_->collision_aware_ik, SIGNAL(stateChanged(int)), this, SIGNAL(configChanged()));
200 connect(ui_->approximate_ik, SIGNAL(stateChanged(int)), this, SIGNAL(configChanged()));
201
202 connect(ui_->wcenter_x, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
203 connect(ui_->wcenter_y, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
204 connect(ui_->wcenter_z, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
205 connect(ui_->wsize_x, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
206 connect(ui_->wsize_y, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
207 connect(ui_->wsize_z, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
208
209 QShortcut* copy_object_shortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_C), ui_->collision_objects_list);
210 connect(copy_object_shortcut, SIGNAL(activated()), this, SLOT(copySelectedCollisionObject()));
211
212 ui_->reset_db_button->hide();
213 ui_->background_job_progress->hide();
214 ui_->background_job_progress->setMaximum(0);
215
216 ui_->tabWidget->setCurrentIndex(1);
217
218 known_collision_objects_version_ = 0;
219
221
222 object_recognition_client_ = rclcpp_action::create_client<object_recognition_msgs::action::ObjectRecognition>(
224
225 if (object_recognition_client_)
226 {
227 if (!object_recognition_client_->wait_for_action_server(std::chrono::seconds(3)))
228 {
229 RCLCPP_ERROR(logger_, "Action server: %s not available", OBJECT_RECOGNITION_ACTION.c_str());
230 object_recognition_client_.reset();
231 }
232 }
233 // TODO (ddengster): Enable when moveit_ros_perception is ported
234
235 // try
236 // {
237 // const planning_scene_monitor::LockedPlanningSceneRO& ps = planning_display_->getPlanningSceneRO();
238 // if (ps)
239 // {
240 // semantic_world_.reset(new moveit::semantic_world::SemanticWorld(ps));
241 // }
242 // else
243 // semantic_world_.reset();
244 // if (semantic_world_)
245 // {
246 // semantic_world_->addTableCallback([this] { updateTables(); });
247 // }
248 // }
249 // catch (std::exception& ex)
250 // {
251 // RCLCPP_ERROR(logger_, "Failed to get semantic world: %s", ex.what());
252 // }
253}
254
259
260void MotionPlanningFrame::approximateIKChanged(int state)
261{
262 planning_display_->useApproximateIK(state == Qt::Checked);
263}
264
265void MotionPlanningFrame::setItemSelectionInList(const std::string& item_name, bool selection, QListWidget* list)
266{
267 QList<QListWidgetItem*> found_items = list->findItems(QString(item_name.c_str()), Qt::MatchExactly);
268 for (QListWidgetItem* found_item : found_items)
269 found_item->setSelected(selection);
270}
271
272void MotionPlanningFrame::allowExternalProgramCommunication(bool enable)
273{
274 // This is needed to prevent UI event (resuming the options) triggered
275 // before getRobotInteraction() is loaded and ready
276 if (first_time_)
277 return;
278
279 planning_display_->getRobotInteraction()->toggleMoveInteractiveMarkerTopic(enable);
281 if (enable)
282 {
283 using std::placeholders::_1;
284 plan_subscriber_ = node_->create_subscription<std_msgs::msg::Empty>(
285 "/rviz/moveit/plan", rclcpp::SystemDefaultsQoS(),
286 [this](const std_msgs::msg::Empty::ConstSharedPtr& msg) { return remotePlanCallback(msg); });
287 execute_subscriber_ = node_->create_subscription<std_msgs::msg::Empty>(
288 "/rviz/moveit/execute", rclcpp::SystemDefaultsQoS(),
289 [this](const std_msgs::msg::Empty::ConstSharedPtr& msg) { return remoteExecuteCallback(msg); });
290 stop_subscriber_ = node_->create_subscription<std_msgs::msg::Empty>(
291 "/rviz/moveit/stop", rclcpp::SystemDefaultsQoS(),
292 [this](const std_msgs::msg::Empty::ConstSharedPtr& msg) { return remoteStopCallback(msg); });
293 update_start_state_subscriber_ = node_->create_subscription<std_msgs::msg::Empty>(
294 "/rviz/moveit/update_start_state", rclcpp::SystemDefaultsQoS(),
295 [this](const std_msgs::msg::Empty::ConstSharedPtr& msg) { return remoteUpdateStartStateCallback(msg); });
296 update_goal_state_subscriber_ = node_->create_subscription<std_msgs::msg::Empty>(
297 "/rviz/moveit/update_goal_state", rclcpp::SystemDefaultsQoS(),
298 [this](const std_msgs::msg::Empty::ConstSharedPtr& msg) { return remoteUpdateGoalStateCallback(msg); });
299 update_custom_start_state_subscriber_ = node_->create_subscription<moveit_msgs::msg::RobotState>(
300 "/rviz/moveit/update_custom_start_state", rclcpp::SystemDefaultsQoS(),
301 [this](const moveit_msgs::msg::RobotState::ConstSharedPtr& msg) {
302 return remoteUpdateCustomStartStateCallback(msg);
303 });
304 update_custom_goal_state_subscriber_ = node_->create_subscription<moveit_msgs::msg::RobotState>(
305 "/rviz/moveit/update_custom_goal_state", rclcpp::SystemDefaultsQoS(),
306 [this](const moveit_msgs::msg::RobotState::ConstSharedPtr& msg) {
307 return remoteUpdateCustomGoalStateCallback(msg);
308 });
309 }
310 else
311 { // disable
312 plan_subscriber_.reset();
313 execute_subscriber_.reset();
314 stop_subscriber_.reset();
315 update_start_state_subscriber_.reset();
316 update_goal_state_subscriber_.reset();
317 update_custom_start_state_subscriber_.reset();
318 update_custom_goal_state_subscriber_.reset();
319 }
320}
321
322void MotionPlanningFrame::fillPlanningGroupOptions()
323{
324 const QSignalBlocker planning_group_blocker(ui_->planning_group_combo_box);
325 ui_->planning_group_combo_box->clear();
326
327 const moveit::core::RobotModelConstPtr& kmodel = planning_display_->getRobotModel();
328 for (const std::string& group_name : kmodel->getJointModelGroupNames())
329 ui_->planning_group_combo_box->addItem(QString::fromStdString(group_name));
330}
331
332void MotionPlanningFrame::fillStateSelectionOptions()
333{
334 const QSignalBlocker start_state_blocker(ui_->start_state_combo_box);
335 const QSignalBlocker goal_state_blocker(ui_->goal_state_combo_box);
336 ui_->start_state_combo_box->clear();
337 ui_->goal_state_combo_box->clear();
338
340 return;
341
342 const moveit::core::RobotModelConstPtr& robot_model = planning_display_->getRobotModel();
343 std::string group = planning_display_->getCurrentPlanningGroup();
344 if (group.empty())
345 return;
346 const moveit::core::JointModelGroup* jmg = robot_model->getJointModelGroup(group);
347 if (jmg)
348 {
349 ui_->start_state_combo_box->addItem(QString("<random valid>"));
350 ui_->start_state_combo_box->addItem(QString("<random>"));
351 ui_->start_state_combo_box->addItem(QString("<current>"));
352 ui_->start_state_combo_box->addItem(QString("<same as goal>"));
353 ui_->start_state_combo_box->addItem(QString("<previous>"));
354
355 ui_->goal_state_combo_box->addItem(QString("<random valid>"));
356 ui_->goal_state_combo_box->addItem(QString("<random>"));
357 ui_->goal_state_combo_box->addItem(QString("<current>"));
358 ui_->goal_state_combo_box->addItem(QString("<same as start>"));
359 ui_->goal_state_combo_box->addItem(QString("<previous>"));
360
361 const std::vector<std::string>& known_states = jmg->getDefaultStateNames();
362 if (!known_states.empty())
363 {
364 ui_->start_state_combo_box->insertSeparator(ui_->start_state_combo_box->count());
365 ui_->goal_state_combo_box->insertSeparator(ui_->goal_state_combo_box->count());
366 for (const std::string& known_state : known_states)
367 {
368 ui_->start_state_combo_box->addItem(QString::fromStdString(known_state));
369 ui_->goal_state_combo_box->addItem(QString::fromStdString(known_state));
370 }
371 }
372
373 ui_->start_state_combo_box->setCurrentIndex(2); // default to 'current'
374 ui_->goal_state_combo_box->setCurrentIndex(2); // default to 'current'
375 }
376}
377
378void MotionPlanningFrame::changePlanningGroupHelper()
379{
381 return;
382
383 planning_display_->addMainLoopJob([this] { fillStateSelectionOptions(); });
384 planning_display_->addMainLoopJob([this]() { populateConstraintsList(std::vector<std::string>()); });
385
386 const moveit::core::RobotModelConstPtr& robot_model = planning_display_->getRobotModel();
387 std::string group = planning_display_->getCurrentPlanningGroup();
388 planning_display_->addMainLoopJob([&view = *ui_->planner_param_treeview, group] { view.setGroupName(group); });
389 planning_display_->addMainLoopJob(
390 [=]() { ui_->planning_group_combo_box->setCurrentText(QString::fromStdString(group)); });
391
392 if (!group.empty() && robot_model)
393 {
394 RCLCPP_INFO(logger_, "group %s", group.c_str());
395 if (move_group_ && move_group_->getName() == group)
396 return;
397 RCLCPP_INFO(logger_, "Constructing new MoveGroup connection for group '%s' in namespace '%s'", group.c_str(),
398 planning_display_->getMoveGroupNS().c_str());
400 group, moveit::planning_interface::MoveGroupInterface::ROBOT_DESCRIPTION, planning_display_->getMoveGroupNS());
401 opt.robot_model = robot_model;
402 opt.robot_description.clear();
403 try
404 {
405#ifdef RVIZ_TF1
406 std::shared_ptr<tf2_ros::Buffer> tf_buffer = moveit::planning_interface::getSharedTF();
407#else
408 //@note: tf2 no longer accessible?
409 // /std::shared_ptr<tf2_ros::Buffer> tf_buffer = context_->getFrameManager()->getTF2BufferPtr();
410 std::shared_ptr<tf2_ros::Buffer> tf_buffer = moveit::planning_interface::getSharedTF();
411#endif
412 move_group_ = std::make_shared<moveit::planning_interface::MoveGroupInterface>(
413 node_, opt, tf_buffer, rclcpp::Duration::from_seconds(30));
414 if (planning_scene_storage_)
415 move_group_->setConstraintsDatabase(ui_->database_host->text().toStdString(), ui_->database_port->value());
416 }
417 catch (std::exception& ex)
418 {
419 RCLCPP_ERROR(logger_, "%s", ex.what());
420 }
421 planning_display_->addMainLoopJob([&view = *ui_->planner_param_treeview, this] { view.setMoveGroup(move_group_); });
422 if (move_group_)
423 {
424 move_group_->allowLooking(ui_->allow_looking->isChecked());
425 move_group_->allowReplanning(ui_->allow_replanning->isChecked());
426 bool has_unique_endeffector = !move_group_->getEndEffectorLink().empty();
427 planning_display_->addMainLoopJob([=]() { ui_->use_cartesian_path->setEnabled(has_unique_endeffector); });
428 std::vector<moveit_msgs::msg::PlannerInterfaceDescription> desc;
429 if (move_group_->getInterfaceDescriptions(desc))
430 planning_display_->addMainLoopJob([this, desc] { populatePlannersList(desc); });
431 planning_display_->addBackgroundJob([this]() { populateConstraintsList(); }, "populateConstraintsList");
432
433 if (first_time_)
434 {
435 first_time_ = false;
436 const planning_scene_monitor::LockedPlanningSceneRO& ps = planning_display_->getPlanningSceneRO();
437 if (ps)
438 {
439 planning_display_->setQueryStartState(ps->getCurrentState());
440 planning_display_->setQueryGoalState(ps->getCurrentState());
441 }
442 // This ensures saved UI settings applied after planning_display_ is ready
443 planning_display_->useApproximateIK(ui_->approximate_ik->isChecked());
444 if (ui_->allow_external_program->isChecked())
445 planning_display_->addMainLoopJob([this] { allowExternalProgramCommunication(true); });
446 }
447 }
448 }
449}
450
451void MotionPlanningFrame::clearRobotModel()
452{
453 ui_->planner_param_treeview->setMoveGroup(moveit::planning_interface::MoveGroupInterfacePtr());
454 joints_tab_->clearRobotModel();
455 move_group_.reset();
456}
457
458void MotionPlanningFrame::changePlanningGroup()
459{
460 planning_display_->addBackgroundJob([this] { changePlanningGroupHelper(); }, "Frame::changePlanningGroup");
461 joints_tab_->changePlanningGroup(planning_display_->getCurrentPlanningGroup(),
462 planning_display_->getQueryStartStateHandler(),
463 planning_display_->getQueryGoalStateHandler());
464}
465
466void MotionPlanningFrame::sceneUpdate(planning_scene_monitor::PlanningSceneMonitor::SceneUpdateType update_type)
467{
469 planning_display_->addMainLoopJob([this] { populateCollisionObjectsList(); });
470}
471
472void MotionPlanningFrame::addSceneObject()
473{
474 static const double MIN_VAL = 1e-6;
475
476 if (!planning_display_->getPlanningSceneMonitor())
477 {
478 return;
479 }
480
481 // get size values
482 double x_length = ui_->shape_size_x_spin_box->isEnabled() ? ui_->shape_size_x_spin_box->value() : MIN_VAL;
483 double y_length = ui_->shape_size_y_spin_box->isEnabled() ? ui_->shape_size_y_spin_box->value() : MIN_VAL;
484 double z_length = ui_->shape_size_z_spin_box->isEnabled() ? ui_->shape_size_z_spin_box->value() : MIN_VAL;
485 if (x_length < MIN_VAL || y_length < MIN_VAL || z_length < MIN_VAL)
486 {
487 QMessageBox::warning(this, QString("Dimension is too small"), QString("Size values need to be >= %1").arg(MIN_VAL));
488 return;
489 }
490
491 // by default, name object by shape type
492 std::string selected_shape = ui_->shapes_combo_box->currentText().toStdString();
493 shapes::ShapeConstPtr shape;
494 switch (ui_->shapes_combo_box->currentData().toInt()) // fetch shape ID from current combobox item
495 {
496 case shapes::BOX:
497 shape = std::make_shared<shapes::Box>(x_length, y_length, z_length);
498 break;
499 case shapes::SPHERE:
500 shape = std::make_shared<shapes::Sphere>(0.5 * x_length);
501 break;
502 case shapes::CONE:
503 shape = std::make_shared<shapes::Cone>(0.5 * x_length, z_length);
504 break;
505 case shapes::CYLINDER:
506 shape = std::make_shared<shapes::Cylinder>(0.5 * x_length, z_length);
507 break;
508 case shapes::MESH:
509 {
510 QUrl url;
511 if (ui_->shapes_combo_box->currentText().contains("file"))
512 { // open from file
513 url = QFileDialog::getOpenFileUrl(this, tr("Import Object Mesh"), QString(),
514 "CAD files (*.stl *.obj *.dae);;All files (*.*)");
515 }
516 else
517 { // open from URL
518 url = QInputDialog::getText(this, tr("Import Object Mesh"), tr("URL for file to import from:"),
519 QLineEdit::Normal, QString("http://"));
520 }
521 if (!url.isEmpty())
522 shape = loadMeshResource(url.toString().toStdString());
523 if (!shape)
524 return;
525 // name mesh objects by their file name
526 selected_shape = url.fileName().toStdString();
527 break;
528 }
529 default:
530 QMessageBox::warning(this, QString("Unsupported shape"),
531 QString("The '%1' is not supported.").arg(ui_->shapes_combo_box->currentText()));
532 }
533
534 // find available (initial) name of object
535 int idx = 0;
536 std::string shape_name = selected_shape + "_" + std::to_string(idx);
537 while (planning_display_->getPlanningSceneRO()->getWorld()->hasObject(shape_name))
538 {
539 idx++;
540 shape_name = selected_shape + "_" + std::to_string(idx);
541 }
542
543 // Actually add object to the plugin's PlanningScene
544 {
545 planning_scene_monitor::LockedPlanningSceneRW ps = planning_display_->getPlanningSceneRW();
546 ps->getWorldNonConst()->addToObject(shape_name, shape, Eigen::Isometry3d::Identity());
547 }
548 setLocalSceneEdited();
549
550 planning_display_->addMainLoopJob([this] { populateCollisionObjectsList(); });
551
552 // Automatically select the inserted object so that its IM is displayed
553 planning_display_->addMainLoopJob([this, shape_name, list_widget = ui_->collision_objects_list] {
554 setItemSelectionInList(shape_name, true, list_widget);
555 });
556
557 planning_display_->queueRenderSceneGeometry();
558}
559
560shapes::ShapePtr MotionPlanningFrame::loadMeshResource(const std::string& url)
561{
562 shapes::Mesh* mesh = shapes::createMeshFromResource(url);
563 if (mesh)
564 {
565 // If the object is very large, ask the user if the scale should be reduced.
566 bool object_is_very_large = false;
567 for (unsigned int i = 0; i < mesh->vertex_count; ++i)
568 {
569 if ((abs(mesh->vertices[i * 3 + 0]) > LARGE_MESH_THRESHOLD) ||
570 (abs(mesh->vertices[i * 3 + 1]) > LARGE_MESH_THRESHOLD) ||
571 (abs(mesh->vertices[i * 3 + 2]) > LARGE_MESH_THRESHOLD))
572 {
573 object_is_very_large = true;
574 break;
575 }
576 }
577 if (object_is_very_large)
578 {
579 QMessageBox msg_box;
580 msg_box.setText(
581 "The object is very large (greater than 10 m). The file may be in millimeters instead of meters.");
582 msg_box.setInformativeText("Attempt to fix the size by shrinking the object?");
583 msg_box.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
584 msg_box.setDefaultButton(QMessageBox::Yes);
585 if (msg_box.exec() == QMessageBox::Yes)
586 {
587 for (unsigned int i = 0; i < mesh->vertex_count; ++i)
588 {
589 unsigned int i3 = i * 3;
590 mesh->vertices[i3] *= 0.001;
591 mesh->vertices[i3 + 1] *= 0.001;
592 mesh->vertices[i3 + 2] *= 0.001;
593 }
594 }
595 }
596
597 return shapes::ShapePtr(mesh);
598 }
599 else
600 {
601 QMessageBox::warning(this, QString("Import error"), QString("Unable to import object"));
602 return shapes::ShapePtr();
603 }
604}
605
606void MotionPlanningFrame::enable()
607{
608 ui_->planning_algorithm_combo_box->clear();
609 ui_->library_label->setText("NO PLANNING LIBRARY LOADED");
610 ui_->library_label->setStyleSheet("QLabel { color : red; font: bold }");
611 ui_->object_status->setText("");
612
613 const std::string new_ns = planning_display_->getMoveGroupNS();
614 if (node_->get_namespace() != new_ns)
615 {
616 RCLCPP_INFO(logger_, "MoveGroup namespace changed: %s -> %s. Reloading params.", node_->get_namespace(),
617 new_ns.c_str());
618 initFromMoveGroupNS();
619 }
620
621 // activate the frame
622 if (parentWidget())
623 parentWidget()->show();
624}
625
626// (re)initialize after MotionPlanningDisplay::changedMoveGroupNS()
627// Should be called from constructor and enable() only
628void MotionPlanningFrame::initFromMoveGroupNS()
629{
630 // Create namespace-dependent services, topics, and subscribers
631 clear_octomap_service_client_ = node_->create_client<std_srvs::srv::Empty>(move_group::CLEAR_OCTOMAP_SERVICE_NAME);
632
633 object_recognition_subscriber_ = node_->create_subscription<object_recognition_msgs::msg::RecognizedObjectArray>(
634 "recognized_object_array", rclcpp::SystemDefaultsQoS(),
635 [this](const object_recognition_msgs::msg::RecognizedObjectArray::ConstSharedPtr& msg) {
636 return listenDetectedObjects(msg);
637 });
638
639 planning_scene_publisher_ = node_->create_publisher<moveit_msgs::msg::PlanningScene>("planning_scene", 1);
640 planning_scene_world_publisher_ =
641 node_->create_publisher<moveit_msgs::msg::PlanningSceneWorld>("planning_scene_world", 1);
642
643 // Declare parameter for default planning pipeline
644 if (!node_->has_parameter(planning_display_->getMoveGroupNS() + "default_planning_pipeline"))
645 node_->declare_parameter<std::string>(planning_display_->getMoveGroupNS() + "default_planning_pipeline", "");
646
647 // Query default planning pipeline id
648 node_->get_parameter(planning_display_->getMoveGroupNS() + "default_planning_pipeline", default_planning_pipeline_);
649
650 // Set initial velocity and acceleration scaling factors from ROS parameters
651 double factor;
652 node_->get_parameter_or("robot_description_planning.default_velocity_scaling_factor", factor, 0.1);
653 ui_->velocity_scaling_factor->setValue(factor);
654 node_->get_parameter_or("robot_description_planning.default_acceleration_scaling_factor", factor, 0.1);
655 ui_->acceleration_scaling_factor->setValue(factor);
656
657 // Fetch parameters from private move_group sub space
658 std::string host_param;
659 if (node_->get_parameter("warehouse_host", host_param))
660 {
661 ui_->database_host->setText(QString::fromStdString(host_param));
662 }
663
664 int port;
665 if (node_->get_parameter("warehouse_port", port))
666 {
667 ui_->database_port->setValue(port);
668 }
669}
670
671void MotionPlanningFrame::disable()
672{
673 move_group_.reset();
674 scene_marker_.reset();
675 if (parentWidget())
676 parentWidget()->hide();
677}
678
679void MotionPlanningFrame::tabChanged(int index)
680{
681 if (scene_marker_ && ui_->tabWidget->tabText(index).toStdString() != TAB_OBJECTS)
682 {
683 scene_marker_.reset();
684 }
685 else if (ui_->tabWidget->tabText(index).toStdString() == TAB_OBJECTS)
686 {
687 selectedCollisionObjectChanged();
688 }
689}
690
691void MotionPlanningFrame::updateSceneMarkers(std::chrono::nanoseconds /*wall_dt*/, std::chrono::nanoseconds /*ros_dt*/)
692{
693 if (scene_marker_)
694 scene_marker_->update();
695}
696
697void MotionPlanningFrame::updateSceneMarkers(double wall_dt, double ros_dt)
698{
699 updateSceneMarkers(std::chrono::nanoseconds(std::lround(wall_dt)), std::chrono::nanoseconds(std::lround(ros_dt)));
700}
701
702void MotionPlanningFrame::updateExternalCommunication()
703{
704 if (ui_->allow_external_program->isChecked())
705 {
706 planning_display_->getRobotInteraction()->toggleMoveInteractiveMarkerTopic(true);
707 }
708}
709
710} // namespace moveit_rviz_plugin
const std::vector< std::string > & getDefaultStateNames() const
Get the names of the known default states (as specified in the SRDF)
static const std::string ROBOT_DESCRIPTION
Default ROS parameter name from where to read the robot's URDF. Set to 'robot_description'.
const robot_interaction::RobotInteractionPtr & getRobotInteraction() const
MotionPlanningFrameJointsWidget * joints_tab_
MotionPlanningFrame(const MotionPlanningFrame &)=delete
void addMainLoopJob(const std::function< void()> &job)
queue the execution of this function for the next time the main update() loop gets called
const moveit::core::RobotModelConstPtr & getRobotModel() const
const planning_scene_monitor::PlanningSceneMonitorPtr & getPlanningSceneMonitor()
This is a convenience class for obtaining access to an instance of a locked PlanningScene.
This is a convenience class for obtaining access to an instance of a locked PlanningScene.
@ UPDATE_GEOMETRY
The geometry of the scene was updated. This includes receiving new octomaps, collision objects,...
std::shared_ptr< tf2_ros::Buffer > getSharedTF()
const std::string OBJECT_RECOGNITION_ACTION
Main namespace for MoveIt.
Specification of options to use when constructing the MoveGroupInterface class.