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");
117
118 // connect buttons to actions; each action usually registers the function pointer for the actual computation,
119 // to keep the GUI more responsive (using the background job processing)
120 connect(ui_->plan_button, &QPushButton::clicked, this, &MotionPlanningFrame::planButtonClicked);
121 connect(ui_->execute_button, &QPushButton::clicked, this, &MotionPlanningFrame::executeButtonClicked);
122 connect(ui_->plan_and_execute_button, &QPushButton::clicked, this, &MotionPlanningFrame::planAndExecuteButtonClicked);
123 connect(ui_->stop_button, &QPushButton::clicked, this, &MotionPlanningFrame::stopButtonClicked);
124 connect(ui_->start_state_combo_box, &QComboBox::textActivated, this, &MotionPlanningFrame::startStateTextChanged);
125 connect(ui_->goal_state_combo_box, &QComboBox::textActivated, this, &MotionPlanningFrame::goalStateTextChanged);
126 connect(ui_->planning_group_combo_box, &QComboBox::textActivated, this,
127 &MotionPlanningFrame::planningGroupTextChanged);
128 connect(ui_->database_connect_button, &QPushButton::clicked, this, &MotionPlanningFrame::databaseConnectButtonClicked);
129 connect(ui_->save_scene_button, &QPushButton::clicked, this, &MotionPlanningFrame::saveSceneButtonClicked);
130 connect(ui_->save_query_button, &QPushButton::clicked, this, &MotionPlanningFrame::saveQueryButtonClicked);
131 connect(ui_->delete_scene_button, &QPushButton::clicked, this, &MotionPlanningFrame::deleteSceneButtonClicked);
132 connect(ui_->delete_query_button, &QPushButton::clicked, this, &MotionPlanningFrame::deleteQueryButtonClicked);
133 connect(ui_->planning_scene_tree, &QTreeWidget::itemSelectionChanged, this,
134 &MotionPlanningFrame::planningSceneItemClicked);
135 connect(ui_->load_scene_button, &QPushButton::clicked, this, &MotionPlanningFrame::loadSceneButtonClicked);
136 connect(ui_->load_query_button, &QPushButton::clicked, this, &MotionPlanningFrame::loadQueryButtonClicked);
137 connect(ui_->allow_looking, &QCheckBox::toggled, this, &MotionPlanningFrame::allowLookingToggled);
138 connect(ui_->allow_replanning, &QCheckBox::toggled, this, &MotionPlanningFrame::allowReplanningToggled);
139 connect(ui_->allow_external_program, &QCheckBox::toggled, this,
140 &MotionPlanningFrame::allowExternalProgramCommunication);
141 connect(ui_->planning_pipeline_combo_box, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
142 &MotionPlanningFrame::planningPipelineIndexChanged);
143 connect(ui_->planning_algorithm_combo_box, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
144 &MotionPlanningFrame::planningAlgorithmIndexChanged);
145 connect(ui_->clear_scene_button, &QPushButton::clicked, this, &MotionPlanningFrame::clearScene);
146 connect(ui_->scene_scale, QOverload<int>::of(&QSlider::valueChanged), this, &MotionPlanningFrame::sceneScaleChanged);
147 connect(ui_->scene_scale, &QSlider::sliderPressed, this, &MotionPlanningFrame::sceneScaleStartChange);
148 connect(ui_->scene_scale, &QSlider::sliderReleased, this, &MotionPlanningFrame::sceneScaleEndChange);
149 connect(ui_->remove_object_button, &QPushButton::clicked, this, &MotionPlanningFrame::removeSceneObject);
150 connect(ui_->object_x, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,
151 &MotionPlanningFrame::objectPoseValueChanged);
152 connect(ui_->object_y, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,
153 &MotionPlanningFrame::objectPoseValueChanged);
154 connect(ui_->object_z, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,
155 &MotionPlanningFrame::objectPoseValueChanged);
156 connect(ui_->object_rx, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,
157 &MotionPlanningFrame::objectPoseValueChanged);
158 connect(ui_->object_ry, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,
159 &MotionPlanningFrame::objectPoseValueChanged);
160 connect(ui_->object_rz, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,
161 &MotionPlanningFrame::objectPoseValueChanged);
162 connect(ui_->publish_current_scene_button, &QPushButton::clicked, this, &MotionPlanningFrame::publishScene);
163 connect(ui_->collision_objects_list, &QListWidget::itemSelectionChanged, this,
164 &MotionPlanningFrame::selectedCollisionObjectChanged);
165 connect(ui_->collision_objects_list, &QListWidget::itemChanged, this, &MotionPlanningFrame::collisionObjectChanged);
166 connect(ui_->path_constraints_combo_box, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
167 &MotionPlanningFrame::pathConstraintsIndexChanged);
168 connect(ui_->clear_octomap_button, &QPushButton::clicked, this, &MotionPlanningFrame::onClearOctomapClicked);
169 connect(ui_->planning_scene_tree, &QTreeWidget::itemChanged, this, &MotionPlanningFrame::warehouseItemNameChanged);
170 connect(ui_->reset_db_button, &QPushButton::clicked, this, &MotionPlanningFrame::resetDbButtonClicked);
171
172 connect(ui_->add_object_button, &QPushButton::clicked, this, &MotionPlanningFrame::addSceneObject);
173 connect(ui_->shapes_combo_box, &QComboBox::currentTextChanged, this, &MotionPlanningFrame::shapesComboBoxChanged);
174 connect(ui_->export_scene_geometry_text_button, &QPushButton::clicked, this,
175 &MotionPlanningFrame::exportGeometryAsTextButtonClicked);
176 connect(ui_->import_scene_geometry_text_button, &QPushButton::clicked, this,
177 &MotionPlanningFrame::importGeometryFromTextButtonClicked);
178 connect(ui_->load_state_button, &QPushButton::clicked, this, &MotionPlanningFrame::loadStateButtonClicked);
179 connect(ui_->save_start_state_button, &QPushButton::clicked, this, &MotionPlanningFrame::saveStartStateButtonClicked);
180 connect(ui_->save_goal_state_button, &QPushButton::clicked, this, &MotionPlanningFrame::saveGoalStateButtonClicked);
181 connect(ui_->set_as_start_state_button, &QPushButton::clicked, this,
182 &MotionPlanningFrame::setAsStartStateButtonClicked);
183 connect(ui_->set_as_goal_state_button, &QPushButton::clicked, this, &MotionPlanningFrame::setAsGoalStateButtonClicked);
184 connect(ui_->remove_state_button, &QPushButton::clicked, this, &MotionPlanningFrame::removeStateButtonClicked);
185 connect(ui_->clear_states_button, &QPushButton::clicked, this, &MotionPlanningFrame::clearStatesButtonClicked);
186#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
187 connect(ui_->approximate_ik, &QCheckBox::checkStateChanged, this, &MotionPlanningFrame::approximateIKChanged);
188#else
189 connect(ui_->approximate_ik, &QCheckBox::stateChanged, this, &MotionPlanningFrame::approximateIKChanged);
190#endif
191
192 connect(ui_->detect_objects_button, &QPushButton::clicked, this, &MotionPlanningFrame::detectObjectsButtonClicked);
193 connect(ui_->pick_button, &QPushButton::clicked, this, &MotionPlanningFrame::pickObjectButtonClicked);
194 connect(ui_->place_button, &QPushButton::clicked, this, &MotionPlanningFrame::placeObjectButtonClicked);
195 connect(ui_->detected_objects_list, &QListWidget::itemSelectionChanged, this,
196 &MotionPlanningFrame::selectedDetectedObjectChanged);
197 connect(ui_->detected_objects_list, &QListWidget::itemChanged, this, &MotionPlanningFrame::detectedObjectChanged);
198 connect(ui_->support_surfaces_list, &QListWidget::itemSelectionChanged, this,
199 &MotionPlanningFrame::selectedSupportSurfaceChanged);
200
201 connect(ui_->tabWidget, &QTabWidget::currentChanged, this, &MotionPlanningFrame::tabChanged);
202
203 /* Notice changes to be safed in config file */
204 connect(ui_->database_host, &QLineEdit::textChanged, this, &MotionPlanningFrame::configChanged);
205 connect(ui_->database_port, QOverload<int>::of(&QSpinBox::valueChanged), this, &MotionPlanningFrame::configChanged);
206
207 connect(ui_->planning_time, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,
209 connect(ui_->planning_attempts, QOverload<int>::of(&QSpinBox::valueChanged), this,
211 connect(ui_->velocity_scaling_factor, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,
213 connect(ui_->acceleration_scaling_factor, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,
215
216#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
217 connect(ui_->allow_replanning, &QCheckBox::checkStateChanged, this, &MotionPlanningFrame::configChanged);
218 connect(ui_->allow_looking, &QCheckBox::checkStateChanged, this, &MotionPlanningFrame::configChanged);
219 connect(ui_->allow_external_program, &QCheckBox::checkStateChanged, this, &MotionPlanningFrame::configChanged);
220 connect(ui_->use_cartesian_path, &QCheckBox::checkStateChanged, this, &MotionPlanningFrame::configChanged);
221 connect(ui_->collision_aware_ik, &QCheckBox::checkStateChanged, this, &MotionPlanningFrame::configChanged);
222 connect(ui_->approximate_ik, &QCheckBox::checkStateChanged, this, &MotionPlanningFrame::configChanged);
223#else
224 connect(ui_->allow_replanning, &QCheckBox::stateChanged, this, &MotionPlanningFrame::configChanged);
225 connect(ui_->allow_looking, &QCheckBox::stateChanged, this, &MotionPlanningFrame::configChanged);
226 connect(ui_->allow_external_program, &QCheckBox::stateChanged, this, &MotionPlanningFrame::configChanged);
227 connect(ui_->use_cartesian_path, &QCheckBox::stateChanged, this, &MotionPlanningFrame::configChanged);
228 connect(ui_->collision_aware_ik, &QCheckBox::stateChanged, this, &MotionPlanningFrame::configChanged);
229 connect(ui_->approximate_ik, &QCheckBox::stateChanged, this, &MotionPlanningFrame::configChanged);
230#endif
231
232 connect(ui_->wcenter_x, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,
234 connect(ui_->wcenter_y, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,
236 connect(ui_->wcenter_z, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,
238 connect(ui_->wsize_x, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &MotionPlanningFrame::configChanged);
239 connect(ui_->wsize_y, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &MotionPlanningFrame::configChanged);
240 connect(ui_->wsize_z, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &MotionPlanningFrame::configChanged);
241
242 QShortcut* copy_object_shortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_C), ui_->collision_objects_list);
243 connect(copy_object_shortcut, &QShortcut::activated, this, &MotionPlanningFrame::copySelectedCollisionObject);
244
245 ui_->reset_db_button->hide();
246 ui_->background_job_progress->hide();
247 ui_->background_job_progress->setMaximum(0);
248
249 ui_->tabWidget->setCurrentIndex(1);
250
251 known_collision_objects_version_ = 0;
252
254
255 object_recognition_client_ = rclcpp_action::create_client<object_recognition_msgs::action::ObjectRecognition>(
257
258 if (object_recognition_client_)
259 {
260 if (!object_recognition_client_->wait_for_action_server(std::chrono::seconds(3)))
261 {
262 RCLCPP_ERROR(logger_, "Action server: %s not available", OBJECT_RECOGNITION_ACTION.c_str());
263 object_recognition_client_.reset();
264 }
265 }
266 // TODO (ddengster): Enable when moveit_ros_perception is ported
267
268 // try
269 // {
270 // const planning_scene_monitor::LockedPlanningSceneRO& ps = planning_display_->getPlanningSceneRO();
271 // if (ps)
272 // {
273 // semantic_world_.reset(new moveit::semantic_world::SemanticWorld(ps));
274 // }
275 // else
276 // semantic_world_.reset();
277 // if (semantic_world_)
278 // {
279 // semantic_world_->addTableCallback([this] { updateTables(); });
280 // }
281 // }
282 // catch (std::exception& ex)
283 // {
284 // RCLCPP_ERROR(logger_, "Failed to get semantic world: %s", ex.what());
285 // }
286}
287
292
293void MotionPlanningFrame::approximateIKChanged(int state)
294{
295 planning_display_->useApproximateIK(state == Qt::Checked);
296}
297
298void MotionPlanningFrame::setItemSelectionInList(const std::string& item_name, bool selection, QListWidget* list)
299{
300 QList<QListWidgetItem*> found_items = list->findItems(QString(item_name.c_str()), Qt::MatchExactly);
301 for (QListWidgetItem* found_item : found_items)
302 found_item->setSelected(selection);
303}
304
305void MotionPlanningFrame::allowExternalProgramCommunication(bool enable)
306{
307 // This is needed to prevent UI event (resuming the options) triggered
308 // before getRobotInteraction() is loaded and ready
309 if (first_time_)
310 return;
311
312 planning_display_->getRobotInteraction()->toggleMoveInteractiveMarkerTopic(enable);
313 planning_display_->toggleSelectPlanningGroupSubscription(enable);
314 if (enable)
315 {
316 using std::placeholders::_1;
317 plan_subscriber_ = node_->create_subscription<std_msgs::msg::Empty>(
318 "/rviz/moveit/plan", rclcpp::SystemDefaultsQoS(),
319 [this](const std_msgs::msg::Empty::ConstSharedPtr& msg) { return remotePlanCallback(msg); });
320 execute_subscriber_ = node_->create_subscription<std_msgs::msg::Empty>(
321 "/rviz/moveit/execute", rclcpp::SystemDefaultsQoS(),
322 [this](const std_msgs::msg::Empty::ConstSharedPtr& msg) { return remoteExecuteCallback(msg); });
323 stop_subscriber_ = node_->create_subscription<std_msgs::msg::Empty>(
324 "/rviz/moveit/stop", rclcpp::SystemDefaultsQoS(),
325 [this](const std_msgs::msg::Empty::ConstSharedPtr& msg) { return remoteStopCallback(msg); });
326 update_start_state_subscriber_ = node_->create_subscription<std_msgs::msg::Empty>(
327 "/rviz/moveit/update_start_state", rclcpp::SystemDefaultsQoS(),
328 [this](const std_msgs::msg::Empty::ConstSharedPtr& msg) { return remoteUpdateStartStateCallback(msg); });
329 update_goal_state_subscriber_ = node_->create_subscription<std_msgs::msg::Empty>(
330 "/rviz/moveit/update_goal_state", rclcpp::SystemDefaultsQoS(),
331 [this](const std_msgs::msg::Empty::ConstSharedPtr& msg) { return remoteUpdateGoalStateCallback(msg); });
332 update_custom_start_state_subscriber_ = node_->create_subscription<moveit_msgs::msg::RobotState>(
333 "/rviz/moveit/update_custom_start_state", rclcpp::SystemDefaultsQoS(),
334 [this](const moveit_msgs::msg::RobotState::ConstSharedPtr& msg) {
335 return remoteUpdateCustomStartStateCallback(msg);
336 });
337 update_custom_goal_state_subscriber_ = node_->create_subscription<moveit_msgs::msg::RobotState>(
338 "/rviz/moveit/update_custom_goal_state", rclcpp::SystemDefaultsQoS(),
339 [this](const moveit_msgs::msg::RobotState::ConstSharedPtr& msg) {
340 return remoteUpdateCustomGoalStateCallback(msg);
341 });
342 }
343 else
344 { // disable
345 plan_subscriber_.reset();
346 execute_subscriber_.reset();
347 stop_subscriber_.reset();
348 update_start_state_subscriber_.reset();
349 update_goal_state_subscriber_.reset();
350 update_custom_start_state_subscriber_.reset();
351 update_custom_goal_state_subscriber_.reset();
352 }
353}
354
355void MotionPlanningFrame::fillPlanningGroupOptions()
356{
357 const QSignalBlocker planning_group_blocker(ui_->planning_group_combo_box);
358 ui_->planning_group_combo_box->clear();
359
360 const moveit::core::RobotModelConstPtr& kmodel = planning_display_->getRobotModel();
361 for (const std::string& group_name : kmodel->getJointModelGroupNames())
362 ui_->planning_group_combo_box->addItem(QString::fromStdString(group_name));
363}
364
365void MotionPlanningFrame::fillStateSelectionOptions()
366{
367 const QSignalBlocker start_state_blocker(ui_->start_state_combo_box);
368 const QSignalBlocker goal_state_blocker(ui_->goal_state_combo_box);
369 ui_->start_state_combo_box->clear();
370 ui_->goal_state_combo_box->clear();
371
372 if (!planning_display_->getPlanningSceneMonitor())
373 return;
374
375 const moveit::core::RobotModelConstPtr& robot_model = planning_display_->getRobotModel();
376 std::string group = planning_display_->getCurrentPlanningGroup();
377 if (group.empty())
378 return;
379 const moveit::core::JointModelGroup* jmg = robot_model->getJointModelGroup(group);
380 if (jmg)
381 {
382 ui_->start_state_combo_box->addItem(QString("<random valid>"));
383 ui_->start_state_combo_box->addItem(QString("<random>"));
384 ui_->start_state_combo_box->addItem(QString("<current>"));
385 ui_->start_state_combo_box->addItem(QString("<same as goal>"));
386 ui_->start_state_combo_box->addItem(QString("<previous>"));
387
388 ui_->goal_state_combo_box->addItem(QString("<random valid>"));
389 ui_->goal_state_combo_box->addItem(QString("<random>"));
390 ui_->goal_state_combo_box->addItem(QString("<current>"));
391 ui_->goal_state_combo_box->addItem(QString("<same as start>"));
392 ui_->goal_state_combo_box->addItem(QString("<previous>"));
393
394 const std::vector<std::string>& known_states = jmg->getDefaultStateNames();
395 if (!known_states.empty())
396 {
397 ui_->start_state_combo_box->insertSeparator(ui_->start_state_combo_box->count());
398 ui_->goal_state_combo_box->insertSeparator(ui_->goal_state_combo_box->count());
399 for (const std::string& known_state : known_states)
400 {
401 ui_->start_state_combo_box->addItem(QString::fromStdString(known_state));
402 ui_->goal_state_combo_box->addItem(QString::fromStdString(known_state));
403 }
404 }
405
406 ui_->start_state_combo_box->setCurrentIndex(2); // default to 'current'
407 ui_->goal_state_combo_box->setCurrentIndex(2); // default to 'current'
408 }
409}
410
411void MotionPlanningFrame::changePlanningGroupHelper()
412{
413 if (!planning_display_->getPlanningSceneMonitor())
414 return;
415
416 planning_display_->addMainLoopJob([this] { fillStateSelectionOptions(); });
417 planning_display_->addMainLoopJob([this]() { populateConstraintsList(std::vector<std::string>()); });
418
419 const moveit::core::RobotModelConstPtr& robot_model = planning_display_->getRobotModel();
420 std::string group = planning_display_->getCurrentPlanningGroup();
421 planning_display_->addMainLoopJob([&view = *ui_->planner_param_treeview, group] { view.setGroupName(group); });
422 planning_display_->addMainLoopJob(
423 [this, group]() { ui_->planning_group_combo_box->setCurrentText(QString::fromStdString(group)); });
424
425 if (!group.empty() && robot_model)
426 {
427 RCLCPP_INFO(logger_, "group %s", group.c_str());
428 if (move_group_ && move_group_->getName() == group)
429 return;
430 RCLCPP_INFO(logger_, "Constructing new MoveGroup connection for group '%s' in namespace '%s'", group.c_str(),
431 planning_display_->getMoveGroupNS().c_str());
432 moveit::planning_interface::MoveGroupInterface::Options opt(
434 opt.robot_model = robot_model;
435 opt.robot_description.clear();
436 try
437 {
438#ifdef RVIZ_TF1
439 std::shared_ptr<tf2_ros::Buffer> tf_buffer = moveit::planning_interface::getSharedTF();
440#else
441 //@note: tf2 no longer accessible?
442 // /std::shared_ptr<tf2_ros::Buffer> tf_buffer = context_->getFrameManager()->getTF2BufferPtr();
443 std::shared_ptr<tf2_ros::Buffer> tf_buffer = moveit::planning_interface::getSharedTF();
444#endif
445 move_group_ = std::make_shared<moveit::planning_interface::MoveGroupInterface>(
446 node_, opt, tf_buffer, rclcpp::Duration::from_seconds(30));
448 move_group_->setConstraintsDatabase(ui_->database_host->text().toStdString(), ui_->database_port->value());
449 }
450 catch (std::exception& ex)
451 {
452 RCLCPP_ERROR(logger_, "%s", ex.what());
453 }
454 planning_display_->addMainLoopJob([&view = *ui_->planner_param_treeview, this] { view.setMoveGroup(move_group_); });
455 if (move_group_)
456 {
457 move_group_->allowLooking(ui_->allow_looking->isChecked());
458 move_group_->allowReplanning(ui_->allow_replanning->isChecked());
459 bool has_unique_endeffector = !move_group_->getEndEffectorLink().empty();
460 planning_display_->addMainLoopJob(
461 [this, has_unique_endeffector]() { ui_->use_cartesian_path->setEnabled(has_unique_endeffector); });
462 std::vector<moveit_msgs::msg::PlannerInterfaceDescription> desc;
463 if (move_group_->getInterfaceDescriptions(desc))
464 planning_display_->addMainLoopJob([this, desc] { populatePlannersList(desc); });
465 planning_display_->addBackgroundJob([this]() { populateConstraintsList(); }, "populateConstraintsList");
466
467 if (first_time_)
468 {
469 first_time_ = false;
470 const planning_scene_monitor::LockedPlanningSceneRO& ps = planning_display_->getPlanningSceneRO();
471 if (ps)
472 {
473 planning_display_->setQueryStartState(ps->getCurrentState());
474 planning_display_->setQueryGoalState(ps->getCurrentState());
475 }
476 // This ensures saved UI settings applied after planning_display_ is ready
477 planning_display_->useApproximateIK(ui_->approximate_ik->isChecked());
478 if (ui_->allow_external_program->isChecked())
479 planning_display_->addMainLoopJob([this] { allowExternalProgramCommunication(true); });
480 }
481 }
482 }
483}
484
486{
487 ui_->planner_param_treeview->setMoveGroup(moveit::planning_interface::MoveGroupInterfacePtr());
488 joints_tab_->clearRobotModel();
489 move_group_.reset();
490}
491
493{
494 planning_display_->addBackgroundJob([this] { changePlanningGroupHelper(); }, "Frame::changePlanningGroup");
495 joints_tab_->changePlanningGroup(planning_display_->getCurrentPlanningGroup(),
496 planning_display_->getQueryStartStateHandler(),
497 planning_display_->getQueryGoalStateHandler());
498}
499
501{
503 planning_display_->addMainLoopJob([this] { populateCollisionObjectsList(); });
504}
505
506void MotionPlanningFrame::addSceneObject()
507{
508 static const double MIN_VAL = 1e-6;
509
511 {
512 return;
513 }
514
515 // get size values
516 double x_length = ui_->shape_size_x_spin_box->isEnabled() ? ui_->shape_size_x_spin_box->value() : MIN_VAL;
517 double y_length = ui_->shape_size_y_spin_box->isEnabled() ? ui_->shape_size_y_spin_box->value() : MIN_VAL;
518 double z_length = ui_->shape_size_z_spin_box->isEnabled() ? ui_->shape_size_z_spin_box->value() : MIN_VAL;
519 if (x_length < MIN_VAL || y_length < MIN_VAL || z_length < MIN_VAL)
520 {
521 QMessageBox::warning(this, QString("Dimension is too small"), QString("Size values need to be >= %1").arg(MIN_VAL));
522 return;
523 }
524
525 // by default, name object by shape type
526 std::string selected_shape = ui_->shapes_combo_box->currentText().toStdString();
527 shapes::ShapeConstPtr shape;
528 switch (ui_->shapes_combo_box->currentData().toInt()) // fetch shape ID from current combobox item
529 {
530 case shapes::BOX:
531 shape = std::make_shared<shapes::Box>(x_length, y_length, z_length);
532 break;
533 case shapes::SPHERE:
534 shape = std::make_shared<shapes::Sphere>(0.5 * x_length);
535 break;
536 case shapes::CONE:
537 shape = std::make_shared<shapes::Cone>(0.5 * x_length, z_length);
538 break;
539 case shapes::CYLINDER:
540 shape = std::make_shared<shapes::Cylinder>(0.5 * x_length, z_length);
541 break;
542 case shapes::MESH:
543 {
544 QUrl url;
545 if (ui_->shapes_combo_box->currentText().contains("file"))
546 { // open from file
547 url = QFileDialog::getOpenFileUrl(this, tr("Import Object Mesh"), QString(),
548 "CAD files (*.stl *.obj *.dae);;All files (*.*)");
549 }
550 else
551 { // open from URL
552 url = QInputDialog::getText(this, tr("Import Object Mesh"), tr("URL for file to import from:"),
553 QLineEdit::Normal, QString("http://"));
554 }
555 if (!url.isEmpty())
556 shape = loadMeshResource(url.toString().toStdString());
557 if (!shape)
558 return;
559 // name mesh objects by their file name
560 selected_shape = url.fileName().toStdString();
561 break;
562 }
563 default:
564 QMessageBox::warning(this, QString("Unsupported shape"),
565 QString("The '%1' is not supported.").arg(ui_->shapes_combo_box->currentText()));
566 }
567
568 // find available (initial) name of object
569 int idx = 0;
570 std::string shape_name = selected_shape + "_" + std::to_string(idx);
571 while (planning_display_->getPlanningSceneRO()->getWorld()->hasObject(shape_name))
572 {
573 idx++;
574 shape_name = selected_shape + "_" + std::to_string(idx);
575 }
576
577 // Actually add object to the plugin's PlanningScene
578 {
579 planning_scene_monitor::LockedPlanningSceneRW ps = planning_display_->getPlanningSceneRW();
580 ps->getWorldNonConst()->addToObject(shape_name, shape, Eigen::Isometry3d::Identity());
581 }
582 setLocalSceneEdited();
583
584 planning_display_->addMainLoopJob([this] { populateCollisionObjectsList(); });
585
586 // Automatically select the inserted object so that its IM is displayed
587 planning_display_->addMainLoopJob([this, shape_name, list_widget = ui_->collision_objects_list] {
588 setItemSelectionInList(shape_name, true, list_widget);
589 });
590
591 planning_display_->queueRenderSceneGeometry();
592}
593
594shapes::ShapePtr MotionPlanningFrame::loadMeshResource(const std::string& url)
595{
596 shapes::Mesh* mesh = shapes::createMeshFromResource(url);
597 if (mesh)
598 {
599 // If the object is very large, ask the user if the scale should be reduced.
600 bool object_is_very_large = false;
601 for (unsigned int i = 0; i < mesh->vertex_count; ++i)
602 {
603 if ((abs(mesh->vertices[i * 3 + 0]) > LARGE_MESH_THRESHOLD) ||
604 (abs(mesh->vertices[i * 3 + 1]) > LARGE_MESH_THRESHOLD) ||
605 (abs(mesh->vertices[i * 3 + 2]) > LARGE_MESH_THRESHOLD))
606 {
607 object_is_very_large = true;
608 break;
609 }
610 }
611 if (object_is_very_large)
612 {
613 QMessageBox msg_box;
614 msg_box.setText(
615 "The object is very large (greater than 10 m). The file may be in millimeters instead of meters.");
616 msg_box.setInformativeText("Attempt to fix the size by shrinking the object?");
617 msg_box.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
618 msg_box.setDefaultButton(QMessageBox::Yes);
619 if (msg_box.exec() == QMessageBox::Yes)
620 {
621 for (unsigned int i = 0; i < mesh->vertex_count; ++i)
622 {
623 unsigned int i3 = i * 3;
624 mesh->vertices[i3] *= 0.001;
625 mesh->vertices[i3 + 1] *= 0.001;
626 mesh->vertices[i3 + 2] *= 0.001;
627 }
628 }
629 }
630
631 return shapes::ShapePtr(mesh);
632 }
633 else
634 {
635 QMessageBox::warning(this, QString("Import error"), QString("Unable to import object"));
636 return shapes::ShapePtr();
637 }
638}
639
641{
642 ui_->planning_algorithm_combo_box->clear();
643 ui_->library_label->setText("NO PLANNING LIBRARY LOADED");
644 ui_->library_label->setStyleSheet("QLabel { color : red; font: bold }");
645 ui_->object_status->setText("");
646
647 const std::string new_ns = planning_display_->getMoveGroupNS();
648 if (node_->get_namespace() != new_ns)
649 {
650 RCLCPP_INFO(logger_, "MoveGroup namespace changed: %s -> %s. Reloading params.", node_->get_namespace(),
651 new_ns.c_str());
653 }
654
655 // activate the frame
656 if (parentWidget())
657 parentWidget()->show();
658}
659
660// (re)initialize after MotionPlanningDisplay::changedMoveGroupNS()
661// Should be called from constructor and enable() only
663{
664 // Create namespace-dependent services, topics, and subscribers
665 clear_octomap_service_client_ = node_->create_client<std_srvs::srv::Empty>(move_group::CLEAR_OCTOMAP_SERVICE_NAME);
666
667 object_recognition_subscriber_ = node_->create_subscription<object_recognition_msgs::msg::RecognizedObjectArray>(
668 "recognized_object_array", rclcpp::SystemDefaultsQoS(),
669 [this](const object_recognition_msgs::msg::RecognizedObjectArray::ConstSharedPtr& msg) {
670 return listenDetectedObjects(msg);
671 });
672
673 planning_scene_publisher_ = node_->create_publisher<moveit_msgs::msg::PlanningScene>("planning_scene", 1);
674 planning_scene_world_publisher_ =
675 node_->create_publisher<moveit_msgs::msg::PlanningSceneWorld>("planning_scene_world", 1);
676
677 // Declare parameter for default planning pipeline
678 if (!node_->has_parameter(planning_display_->getMoveGroupNS() + "default_planning_pipeline"))
679 node_->declare_parameter<std::string>(planning_display_->getMoveGroupNS() + "default_planning_pipeline", "");
680
681 // Query default planning pipeline id
682 node_->get_parameter(planning_display_->getMoveGroupNS() + "default_planning_pipeline", default_planning_pipeline_);
683
684 // Set initial velocity and acceleration scaling factors from ROS parameters
685 double factor;
686 node_->get_parameter_or("robot_description_planning.default_velocity_scaling_factor", factor, 0.1);
687 ui_->velocity_scaling_factor->setValue(factor);
688 node_->get_parameter_or("robot_description_planning.default_acceleration_scaling_factor", factor, 0.1);
689 ui_->acceleration_scaling_factor->setValue(factor);
690
691 // Fetch parameters from private move_group sub space
692 std::string host_param;
693 if (node_->get_parameter("warehouse_host", host_param))
694 {
695 ui_->database_host->setText(QString::fromStdString(host_param));
696 }
697
698 int port;
699 if (node_->get_parameter("warehouse_port", port))
700 {
701 ui_->database_port->setValue(port);
702 }
703}
704
706{
707 move_group_.reset();
708 scene_marker_.reset();
709 if (parentWidget())
710 parentWidget()->hide();
711}
712
713void MotionPlanningFrame::tabChanged(int index)
714{
715 if (scene_marker_ && ui_->tabWidget->tabText(index).toStdString() != TAB_OBJECTS)
716 {
717 scene_marker_.reset();
718 }
719 else if (ui_->tabWidget->tabText(index).toStdString() == TAB_OBJECTS)
720 {
721 selectedCollisionObjectChanged();
722 }
723}
724
725void MotionPlanningFrame::updateSceneMarkers(std::chrono::nanoseconds /*wall_dt*/, std::chrono::nanoseconds /*ros_dt*/)
726{
727 if (scene_marker_)
728 scene_marker_->update();
729}
730
731void MotionPlanningFrame::updateSceneMarkers(double wall_dt, double ros_dt)
732{
733 updateSceneMarkers(std::chrono::nanoseconds(std::lround(wall_dt)), std::chrono::nanoseconds(std::lround(ros_dt)));
734}
735
737{
738 if (ui_->allow_external_program->isChecked())
739 {
740 planning_display_->getRobotInteraction()->toggleMoveInteractiveMarkerTopic(true);
741 }
742}
743
744} // 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'.
moveit_warehouse::PlanningSceneStoragePtr planning_scene_storage_
std::shared_ptr< rviz_default_plugins::displays::InteractiveMarker > scene_marker_
MotionPlanningFrameJointsWidget * joints_tab_
moveit::planning_interface::MoveGroupInterfacePtr move_group_
void sceneUpdate(planning_scene_monitor::PlanningSceneMonitor::SceneUpdateType update_type)
void updateSceneMarkers(std::chrono::nanoseconds wall_dt, std::chrono::nanoseconds ros_dt)
MotionPlanningFrame(const MotionPlanningFrame &)=delete
const planning_scene_monitor::PlanningSceneMonitorPtr & getPlanningSceneMonitor()
@ 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.